Jayd Saucedo

Blog

Saurdo Directory Lister Update - v1.2
About a year and a half ago, in my last directory lister update, I added a feature that I described as the "ability to define the maximum pixels the thumbnailer should try to thumbnail. (to prevent memory errors)". The way I did this was by having the thumbnailer reject large images which would usually cause PHP to run out of it's preallocated memory. Basically, you define the maximum number of pixels (widthxheight) and it won't thumbnail anything bigger than that. This worked quite well, and I thought it was a very good idea at the time, but I think I might have come up with something better, something that will allow you to thumbnail any size image you want!

The whole reason I resorted to rejecting large images was because the PHP function that causes the memory error doesn't handle its errors very well. What I would have liked to do was recover from one of these memory errors and display a different image in its place, but instead the error kills the script. So there is no way I could have done something like this:

// loading image!
$im = @imagecreatefromjpeg("test-image.jpg");

// oh no! image too big to load into memory!
if(!$im){
	// special function that creates new image and puts the filename in it
	imageCenterString( $THUMBNAIL_WIDTH, $THUMBNAIL_HEIGHT, basename($image), $imageFontSize );
}

Unfortunately, PHP would have none of this! The script will die at the first line and ignore everything after it. It makes sense because if your PHP script is exceeding its memory limit it's not going to continue down the script, it doesn't have enough memory too! Still, I wish the "imagecreatefromjpeg" function had a feature that makes it purge the image when it hit the memory limit.

Someone e-mailed me the other day, asking about why some of their larger images weren't thumbnailing. I kindly explained to him that he'd need to adjust PHP's memory limit to get them to thumbnail, or modify the script to prevent it from trying, but then this got me thinking; is there a way that I can dynamically adjust the memory limit to conform to the needs of the image?

Thankfully, someone had already doled out the details of the algorithm used to calculate memory usage over in the PHP documentation community:

// starting memory usage
$start_mem = memory_get_usage();
// Place the images to load in the following array:
$ffs = array();

// loop through array of images to test
foreach ($images as $image) {
	// get array of image data
	$info = getimagesize($image);
	// load image
	$im = imagecreatefromjpeg($image);
	// get current memory usage (after loading image)
	$mem = memory_get_usage();
	// calculate fudge factor
	$ff = (($mem - $start_mem) /
		 ($info[0] * $info[1] * ($info['bits'] / 8) * $info['channels']));
	// stick fudge factor in cumulative array
	$ffs[] = $ff;
	// remove image from memory
	imagedestroy($im);
	// get new starting memory
	$start_mem = memory_get_usage();
}

// calculate the mean of all the fudge factors
echo 'Mean fudge factor: ' . (array_sum($ffs) / count($ffs));
So, to start out we get the starting memory (which should be very low), then we loop through some images, load those images into memory, figure out how much memory those images are using, and calculate what the the person who made this script refers to as the "fudge factor". This is used to calculate the overhead memory that will be needed. Normally we can just take the (width * height * bpp), but there is also some overhead. So the algorithm to get the fudge factor goes like this (current memory - starting memory) then divide that by (width * height * bpp). After that the script simply adds that to an array, does some garbage collecting, and gets a new starting memory number. Finally, the script will get the mean of all of the fudge factors it has collected.

What do you do with this number? Well, we can now calculate the estimated memory that an image will take to thumbnail. To do that you simply take the (width * height * bpp * the fudge factor), and to set PHP to use that you simply use the ini_set function!

All in all my final implementation into my directory lister looks like this:

// if it is set
if($fudge_factor){
	// set memory limit
	ini_set('memory_limit', (($imgsize[0] * $imgsize[1] * ($imgsize['bits']/8) * $fudge_factor)+5).'M');
}

That's right, it's only 3 lines! It could easily be one if I sacrificed readability. On top of all this, because I know this is sounding complicated for those that aren't programmers, I made a quick and easy form to randomly select images from a directory and calculate the mean fudge factor. I call it the "fudge factor calculator!"

You can download the new version here!