Tuesday, August 11, 2009

THE THUMB NAIL IMAGE CLASS

Images are often of unequal dimensions, and their file sizes can vary greatly. This
inconsistency creates problems when down- loading and displaying a group of images because
one large image can take up the entire screen, dwarfing smaller images. Also, if image files are large, they can slow downloading to an unacceptable pace.
One solution to the problem of inconsistently sized images is to create a thumbnail image class, which creates small images (thumbnails) of equal size. By reducing the quality of an image, this class will be able to further reduce file size and hence download times.
Since we intend to use the DirectoryItems class with directories of images, this additional supporting class takes the next step toward improving the utility of the DirectoryItems class. Furthermore, developing this class should give you a good idea of when a method should be private, how to limit access to data members with set and get methods, how to set default values for data members upon declaration, and how to ensure that resources are disposed of properly.

What Does a Designer Do?

To determine what the DirectoryItems class should do, consider how web designers create thumbnail images. They typically open an image file in a graphic editor, reduce its dimensions (and perhaps its quality), and then resave the file under a different name.
For ease of placement on the screen, images in a group are usually reduced to approximately the same thumbnail size as each other. In other words, the thumbnail for a large image is roughly the same size as the thumbnail for a medium-sized image. The image is typically reduced to a predetermined maximum dimension while constraining its proportions so as not to distort it. This maximum dimension is usually applied to the width or the height, depending upon whether the image’s orientation is landscape or portrait. While this maximum will vary, the maximum size of a thumbnail should be such that it is small enough to download quickly, but large enough for the viewer to form an accurate impression of the full-sized picture. Once created, the thumbnail is typically displayed in a web page, where it might function as a hyperlink to its full-sized counterpart.

Mimicking the Designer

Having considered how the web designer handles thumbnails, we can better determine how a thumbnail class should behave. We will build a class that mimics the way a designer creates a thumbnail but with certain improvements.
When the designer creates a thumbnail, he writes a separate file to disk. While it might make sense in some cases for a class to create thumbnail images only once and then save them to disk, we will create them on the fly, as needed, and output them to the browser without saving them. This approach allows us to create a simpler, and in some respects, a more flexible class. We won’t have to worry about where to store the thumbnails or whether a thumbnail has already been created for a specific image.

NOTE The downside to this approach is that it requires more server-side processing, and, if there are a large number of images per web page, it may degrade server performance. We’ll solve this problem in Chapter 7.

Help from PHP Functions

When creating the thumbnail image class, the equivalent of the designer’s graphic editor is the existing PHP image function library, which contains the tools you need to manipulate common image types. PHP’s imagecreatefrom functions return a resource making it possible to programmatically copy an image, reduce its dimensions, and also reduce its quality if necessary.
Thus, you have your editor and you know how your class should behave.
You know too that you want to preserve the proportions of an image when you create a thumbnail and that you want to reduce all images to the same approximate thumbnail size. You’re all set to start coding.

NOTE The code used in this chapter requires a minimum version 2.0.1 of the GD graphics librar y. This will not be a problem if you are using PHP 5, but it may be if you are following along and creating a class in PHP 4. To determine which version you have, use the phpinfo function or the more specific gd_info.

The ThumbnailImage Class

In the following sections, you’ll examine the entire ThumbnailImage class, interspersing the code with comments.

Data Members

As always, the data members are private. The variable $image holds the actual thumbnail image itself.

private $image;
private $quality = 100;
private $mimetype;
private $imageproperties = array();
private $initialfilesize;

Since, in some cases, you may want to vary the quality of the thumbnail, you create the attribute $quality. For very large files (large in terms of byte count rather than just dimensions), you may need to reduce the quality of an image as well as its size. Give $quality a default value of 100 to indicate no reduction in quality, because in most cases, you will retain the quality of the original image.

NOTE The assignment of a value to $quality shows that data members may be initialized upon declaration, but they must be initialized with constant values. You could not, for instance, invoke a PHP function call or a method.

If you are going to output an image, you need to know whether it’s a
.jpeg, .gif, or .png file, hence the need for a MIME type attribute. Finally, add
$imageproperties, principally to capture the dimensions of the original image. Initialize it as an array (although there is no requirement to do so), because doing so is a nice way to document the data type of this variable.

NOTE Knowing the MIME type makes it easier for the browser to display an image.

Deconstructing the Constructor

As you saw in Chapter 5, the constructor is a magic method that begins with a double underscore and is invoked whenever a new instance of a class is created. The constructor for the ThumbnailImage class is shown in Listing 6-1.

public function __construct($file, $thumbnailsize = 100){
//check file

is_file($file) or die ("File: $file doesn't exist.");
$this->initialfilesize = filesize($file);
$this->imageproperties = getimagesize($file) or die ("Incorrect file_
type.");
// new function image_type_to_mime_type
$this->mimetype = image_type_to_mime_type($this->imageproperties[2]);
//create image
switch($this->imageproperties[2]){
case IMAGETYPE_JPEG:
$this->image = imagecreatefromJPEG($file);
break;
case IMAGETYPE_GIF:
$this->image = imagecreatefromGIF($file);
break;
case IMAGETYPE_PNG:
$this->image = imagecreatefromPNG($file);
break;
default:
die("Couldn't create image.");
}
$this->createThumb($thumbnailsize);
}

Listing 6-1: The constructor for the ThumbnailImage class

The code first checks that the $file passed in is legitimate, and, if so, it retrieves the properties of the image. In addition to file dimensions, the built-in PHP function filesize returns a constant integer that indicates the file’s MIME type. This PHP constant can be converted to a string value by using the image_type_to_mime_type function.

NOTE This function is new to PHP 5, so if you are working in PHP 4, the code needs to be different. This work has been done for you. Download the version 4 files of Chapter 6 to see how the same results are achieved by looking at file extensions. Knowing the MIME type will be necessary when you want to output your image.

The appropriate, image-specific imagecreatefrom function ( ) is called and a resource is returned. The actual thumbnail is created by manipulating this resource in the createThumb method.
Two parameters are passed to the constructor. The parameter $file is required; $thumbnailsize is optional because it has a default value. The $file variable tells your class where to find the image that is to be reduced, and
$thumbnailsize indicates the dimension that it will be reduced to.

Two Ways to Construct an Object

When discussing constructors in Chapter 5, you saw how default values can be assigned to parameters, thus providing flexibility and improving ease of use. The assignment of the value 100 to the variable $thumbnailsize means that the default size of your thumbnail will be 100 pixels.

Because this variable has a default value, you can create a class instance in two different ways. To accept the default thumbnail size, create an object like so:

$thumb = new ThumbnailImage("graphics/My_Picture.jpg");

In this case, the maximum dimension of the thumbnail will be the default value.
To construct a thumbnail of different dimensions, do the following:

$thumb = new ThumbnailImage("graphics/My_Picture.jpg", 250);

Assigning a default value to an argument to the constructor is simply a convenience for users of your class.

NOTE When assigning default values to arguments to a method, you may have as many default values as you wish. However, arguments without a default value should not fol- low those that have a default value; in this particular class, the $path variable should not follow the $thumbnailsize variable.

Internal Behavior—Private Methods

So far you have seen only private data members, but here you encounter your first private method. Private methods relate to the internal workings of a class and can be invoked only from within the class itself. The method that performs the image reduction (see Listing 6-2) is a private method—createThumb— called from within the constructor.

private function createThumb($thumbnailsize){
//array elements for width and height
$srcW = $this->imageproperties[0];
$srcH = $this->imageproperties[1];
//only adjust if larger than max
if($srcW > $thumbnailsize || $srcH > $thumbnailsize){
$reduction = $this->calculateReduction($thumbnailsize);
//get proportions
$desW = $srcW/$reduction;
$desH = $srcH/$reduction;
$copy = imagecreatetruecolor($desW, $desH);
imagecopyresampled($copy,$this->image,0,0,0,0,$desW, $desH, $srcW,
$srcH) or die ("Image copy failed.");
//destroy original imagedestroy($this->image);
$this->image = $copy;
}
}

Listing 6-2: The createThumb method

In this listing, createThumb checks the width and height of the image to determine whether it is greater than the targeted size. If it is, the method creates a reduced copy and overwrites the original image with the copy.
This private method for image reduction is called from the constructor and may only be invoked from within the class. By calling it from within the constructor, it need not be called directly, and the client programmer benefits by having a fully-formed and usable object immediately upon construction.

Must It Be Private?

Suppose for a moment, though, that your intention was to make a number of different-sized reductions of the same image, just as a photographer often makes different-sized copies of the same picture. In this case, it might make sense to save the original image and make the createThumb method public. As such, an image could be recreated at different sizes by repeatedly calling this method and passing the method different values.
In fact, with minimal change to the code and the interface, you could make your class accommodate this scenario and still fulfill its original intention.

A Helper Method

From within the createThumb method, you call another private method,
calculateReduction, shown in Listing 6-3.

private function calculateReduction($thumbnailsize){
$srcW = $this->imageproperties[0];
$srcH = $this->imageproperties[1];
//adjust
if($srcW < $srcH){
$reduction = round($srcH/$thumbnailsize);
}else{
$reduction = round($srcW/$thumbnailsize);
}
return $reduction;
}

Listing 6-3: The calculateReduction method

The calculateReduction method determines whether the height or width of the image is larger and then calculates the percentage reduction based on the targeted thumbnail size. In other words, it determines whether the image’s orientation is landscape or portrait and reduces the image on the appropriate dimension.

NOTE Unlike the createThumb method, the calculateReduction method is inherently private.
It is a helper method that returns the percentage reduction to the createThumb method.

Public Methods

Following the constructor is another public method with the name __destruct. This method is known as a destructor. The double underscore ( ) in front of the function name indicates that this method, like the constructor, is another magic method. Again, it is a method newly introduced in PHP 5. (Recall from Chapter 5 that magic methods happen in the background, like magic.)

public function __destruct(){
if(isset($this->image)){
imagedestroy($this->image);
}
}

While the use of destructors is new with PHP 5, anyone familiar with other OO languages has probably already come across them. As its name suggests, a destructor is the opposite of a constructor. A constructor initializes an object, while a destructor frees up resources that a class may have allocated. Generally, PHP does a good job of cleaning up after itself so destructors are often not strictly necessary. It’s used here to ensure that the image resource is disposed of.

Garbage Collection

Like Java, PHP employs a garbage collector to automatically clean up resources. Because the programmer is not responsible for allocating and freeing memory (as he is in a language like C++, for example), an automated program must free up resources. The garbage collector determines when objects are no longer used and then disposes of them, for example, when they go out of scope. However, using destructors can act as a cue that speeds up garbage collection. In the case of the ThumbnailImage class, the destructor disposes of the reduced image copy by calling the imagedestroy function.
You can call the destructor directly but, as with other magic methods the destructor is designed to be invoked in the background without any inter- vention by the programmer.

Displaying the Image

Next to the constructor, getImage (see Listing 6-4) is the most important method in the ThumbnailImage class, because it actually sends a reduced image to the browser. Its logic is simple: A header is sent to the browser announcing what to expect, followed by the appropriate content.

public function getImage(){
header("Content-type: $this->mimetype");
switch($this->imageproperties[2]){

case IMAGETYPE_JPEG:
imagejpeg($this->image, "", $this->quality);
break;
case IMAGETYPE_GIF: imagegif($this->image); break;
case IMAGETYPE_PNG:
imagepng($this->image, "", $this->quality);
break;
default:
die("Couldn't create image.");
}
}

Listing 6-4: The getImage method

Because .png and .jpeg image types support a reduction in quality, the quality argument is included when the images are output. The proper MIME type is sent to the browser first, and subsequently the image is sent in binary format.

Get and Set Methods

Chapter 5 introduced the concept of private data members and discussed how they create a need for accessor methods (also referred to as get and set methods), which retrieve and change the value of data members. The getMimeType method retrieves the MIME type of your thumbnail image. (Recall that the value returned is a copy and not the original.)

public function getMimeType(){
return $this->mimetype;
}

You need to retrieve the value of the private variable mimetype when you display your thumbnail. Merely retrieving the MIME type can do no harm, but the same cannot be said of setting this value. The MIME type is set in the constructor by looking at an image’s properties. Since this information can be determined programmatically and since an image’s MIME type does not change, there is no need to set this value. Hence, there is no set method to match the get method. To say the same thing in another way, $mimetype is a read-only value and having only a get method contributes to data protection.

Image Quality

On the other hand, it makes sense to have both a set and get method for image quality. The quality property of a ThumbnailImage object is quite different from an image’s MIME type and is not something that must remain fixed. In fact, getting and setting the quality of an image is one of the requirements that you set out to achieve when you designed this class. Let’s first look at the method that sets image quality in Listing 6-5.

public function setQuality($quality){
if ($quality > 100 || $quality < 1){
$quality = 75;
if($this->imageproperties[2] == IMAGETYPE_JPEG || $this-
>imageproperties[2] == IMAGETYPE_PNG){
$this->quality = $quality;
}
}

Listing 6-5: The setQuality method

As you can see in this listing, negative values and values greater
than 100 are prohibited because they are not valid values for image quality. Furthermore, .gif images don’t support alterations of the image quality, so
the second if statement checks for the appropriate image type before changing the quality. A set method is superior to direct access to an object’s properties because values can be tested and rejected, if need be, before they are assigned. A set method allows you to restrict how the variable quality is changed by screening out illegal values.
While the need to control the way in which object properties are
changed is somewhat obvious, retrieving object properties through an accessor method is also superior to directly accessing a public data member. Because you can’t alter the quality of a GIF, there is no need to retrieve it, and the getQuality method (see Listing 6-6) reflects this.

public function getQuality(){
$quality = null;
if($this->imageproperties[2] == IMAGETYPE_JPEG || $this-
>imageproperties[2] == IMAGETYPE_PNG){
$quality = $this->quality;
}
return $quality;
}

Listing 6-6: The getQuality method

Just as the setQuality method restricted changes to the quality of a .gif image, the getQuality method only returns a legitimate value if the image is a .jpeg or .png. Otherwise, null is returned.
Accessor methods are superior to direct access to public data members because they restrict how a variable is changed and how it is retrieved. They help ensure the integrity of your data and the functionality of the class as a whole. Get and set methods allow you to ignore the fact that .gif images don’t support a quality attribute in a way that unfettered public access cannot.

When to Change the Quality

In order to determine if the quality of an image needs reducing, it’s helpful to know a bit more about the image. The getInitialFileSize function returns the image’s original size in bytes. This information helps you decide whether to reduce the quality of an image and, if so, by how much.

public function getInitialFileSize(){
return $this->initialfilesize;
}

The code in this chapter doesn’t actually call this method, but you can imagine the circumstances in which it might be useful.

Displaying a Thumbnail

The process of outputting a series of thumbnail images to the browser occurs in two steps. First, you create a script that outputs an image; then you use this script file as the source for an img tag.
The code in Listing 6-7 shows the script file for outputting an image. It retrieves the path and size from a query string and uses these values to con- struct a thumbnail and then display it (in this chapter’s downloads, this is the file getthumb.php).

<?php
//this file will be the src for an img tag require 'ThumbnailImage.php';
$path = $_GET["path"];
$maxsize = @$_GET["size"];
if(!isset($maxsize)){
$maxsize = 100;
}
if(isset($path)){
$thumb = new ThumbNailImage($path, $maxsize);
$thumb->getImage();
}
?>

Listing 6-7: Constructing and displaying a thumbnail image

When passed a query string describing the path to an image file and the desired image size, this code outputs a thumbnail directly to the browser. The getImage method ( ) tells the browser the MIME type to expect and then sends the image file in binary format.

NOTE Typing getthumb.php?path=graphics/filename.jpg into the browser address bar is equivalent to pointing your browser directly at an image file. However, because you want to output a series of pictures and control their position, you will use this file as the src attribute of an img tag.

Putting It All Together

The short piece of code in Listing 6-8 uses the DirectoryItems class together with the ThumbnailImage class to display all images within a directory, at reduced sizes.

<?php
require 'DirectoryItems.php';

$dc = new DirectoryItems('graphics');
$dc->imagesOnly();
$dc->naturalCaseInsensitiveOrder();
$path = "";
$filearray = $dc->getFileArray();
echo "<div style=\"text-align:center;\">";
echo "Click the filename to view full-sized version.<br />";
//specify size of thumbnail
$size = 100;
foreach ($filearray as $key => $value){
$path = "graphics/".$key;
/*errors in getthumb or in class will result in broken links
- error will not display*/
echo "<img src=\"getthumb.php?path=$path&amp;size=$size\" ". "style=\"border:1px solid black;margin-top:20px;\" ".
"alt= \"$value\" /><br />\n";
echo "<a href=\"$path\" target=\"_blank\" >";
echo "Title: $value</a> <br />\n";
}
echo "</div><br />";
?>

Listing 6-8: Displaying all the images in a directory at reduced size

As shown in Listing 6-8, you first construct a DirectoryItems object and pass it the directory named graphics. You filter non-image files with the imagesOnly function, and the path is passed as a query string to the getthumb.php file, which, in turn, is assigned to the src attribute of an img tag.
This may seem strange at first, but the getthumb.php file contains all the information that the browser needs to display an image. However, if there are any errors in this file or in the thumbnail class file, the image will fail to display, and there will be no warning or error message, regardless of how you have configured your php.ini file. The error message will simply be interpreted as the binary output expected by the img tag.

NOTE In order to see error messages and warnings when debugging the ThumbnailImage class file, you need to call the getthumb.php file directly and not set it as the src for an img tag. Do this by hard-coding an image filename directly into the getthumb.php file and typing getthumb.php in the browser address bar.

Where to Go from Here

Using the ThumbnailImage class enhances your ability to display a directory of images by reducing the size of the images. This is a definite improvement in both aesthetics and performance, because small images download faster and use up less screen real estate.
But what if the image directory contains a few hundred or even a few thousand images? Showing a large number of image files, even if they’re only thumbnails, places unacceptable demands on server and client resources, and creates a web page that is far too long. You need to limit the number of images that display at any one time. Chapter 7 tackles this problem.

1 comments:

Bijayani said...

Hi,

Really a good post.
I am sharing link just for the knowledge purpose where a software engineer has shared a tip on "Reduce image size using PHP GD functions". PHP GD function can be used to reduce the size of the image.

Here is the link for more detail information:
http://www.mindfiresolutions.com/Reduce-image-size-using-PHP-GD-functions-281.php

Thanks,
Bijayani