brianviehland has asked for the wisdom of the Perl Monks concerning the following question:

I wrote a nice image gallery program that uses Image Magick to resize user-uploaded images and create thumbnails for those images at upload. I've been trying to get an alternate version working using GD.pm instead of ImageMagick for those who cannot get their hosts to install IM. Using GD and all the references I can find, I am still having the same problem. Here's whats happening... I load an image into the script using GD and then use the copyResized or the copyResampled function and write the image into the thumbnail file. The script is writing the file, but the thumbnail file is not quite right. The last 10 rows of pixels (sometimes more, sometimes less) come out blank or default gray, while the rest looks fine. I can't seem to find where anyone else has had this problem and I can't seem to find anything wrong with the code. Any ideas? The image uploads successfully prior to this routine and the paths are passed to it successfully. The main section of this routine is below: ________________________________________________________
use CGI qw(:standard); use CGI::Carp qw(fatalsToBrowser); use GD; sub thumbnail { use File::Copy; $maxThumbSize = "150"; $maxImageSize = "600"; ($imageBase,$crap) = split(/\./,$name); $imageBase =~ s/^\s+//g; $imageBase =~ s/\s+$//g; $imageBase =~ s/\"//g; $imageBase =~ s/-//g; $imageBase =~ s/ //g; $imageURL = $albumFolderURL . "/" . $category . "/" . $album . "/Image +s/" . $name; $imagePath = $imageFolder . $S . $name; $categoryCoverPath = $albumFolderPath . $S . $category . $S . "cover.j +pg"; $albumCoverPath = $albumFolderPath . $S . $category . $S . $album . $S + . "cover.jpg"; if ($name =~ m/gif$/) { &error('GIF files are copyrighted through 2004 +'); } if (($name =~ m/jpg$/i) || ($file =~ m/jpeg$/i)) { $sourceImage = GD::Image->newFromJpeg($imagePath); $retype = "no"; } elsif ($name =~ m/png$/i) { open (PNG,imagePath) || &error('Cannot Open PNG File'); $sourceImage = newFromPng GD::Image(PNG) || &error('Cannot Read Fr +om PNG File'); close PNG; $retype = "yes"; } elsif ($name =~ m/bmp$/i) { $sourceImage = GD::Image->newFromWMP("$imagePath"); $retype = "yes"; } ($uploadWidth,$uploadHeight) = $sourceImage->getBounds(); # Resize Original if too big ############################# if ((($uploadWidth > $maxImageSize) || ($uploadHeight > $maxImageSize) +) || ($retype eq "yes")) { if ($uploadWidth > $uploadHeight) { $scalefactor=$maxImageSize/$up +loadWidth; } else { $scalefactor=$maxImageSize/$uploadHeight; } $fileWidth = int($uploadWidth*$scalefactor); $fileHeight = int($up +loadHeight*$scalefactor); $resizedOriginal = new GD::Image($fileWidth, $fileHeight); $resizedOriginal->copyResampled($sourceImage,0,0,0,0,$fileWidth,$fileH +eight,$uploadWidth,$uploadHeight); $newImagePath = $imageFolder . $S . $imageBase . ".jpg"; unlink("$imagePath"); open(IMAGE, ">$newImagePath") or &error('Cannot Open New File'); if ($opsys eq "dos") { binmode IMAGE; } else { flock(IMAGE, 2); } print IMAGE $resizedOriginal->jpeg(75); close(IMAGE) or &error('Cannot Close New File'); chmod(0644, $newImagePath); } # else tranfer images size to new variables else { $fileWidth=$uploadWidth; $fileHeight=$uploadHeight; $newImagePa +th = $imagePath; } # Create Thumbnail if necessary ############################### if ((($fileWidth gt $maxThumbSize) || ($fileHeight gt $maxThumbSize)) +|| ($retype eq "yes")) { if ($fileWidth gt $fileHeight) { $scalefactor=$maxThumbSize/$fileW +idth; } else { $scalefactor=$maxThumbSize/$fileHeight; } $thumbWidth = int($fileWidth*$scalefactor); $thumbHeight = int($fi +leHeight*$scalefactor); } else { $thumbWidth=$fileWidth; $thumbHeight=$fileHeight; } print "Make Thumbnail, FW = $fileWidth, FH = $fileHeight, TW = $thumbW +idth, TH = $thumbHeight<Br>"; $thumbnailImage = new GD::Image($thumbWidth, $thumbHeight); $thumbnailImage->copyResampled($sourceImage,0,0,0,0,$thumbWidth,$thumb +Height,$fileWidth,$fileHeight); $thumbPath = $thumbFolder . $S . $imageBase . "-" . $fileWidth . "x" . + $fileHeight . ".jpg"; if (-f $thumbPath) { unlink("$thumbPath"); } open(THUMB, ">$thumbPath") or &error('Cannot Open New Thumbnail'); if ($opsys eq "dos") { binmode THUMB; } else { flock(THUMB, 2); } if ((($fileWidth gt $maxThumbSize) || ($fileHeight gt $maxThumbSize)) +|| ($retype eq "yes")) { print THUMB $thumbnailImage->jpeg(75); } else { print THUMB $sourceImage->jpeg(75); } close(THUMB) or &error('Cannot Close New Thumbnail'); chmod(0644, $thumbPath); }

Replies are listed 'Best First'.
Re: GD.pm copyResized / copyResampled errors
by Roger (Parson) on Jan 29, 2004 at 06:44 UTC
    Ah I see, it's line 173 of your code! (Abigail joke?) Just kidding. :-)

    Can you show us your code? Otherwise we can only guess what's happenning... The copyResized and copyResampled methods will stretch or shrink the image to fit the given thumbnail dimension, but could be something to do with not closing the image file properly? Binmode on Windows platform?

      I've updated the post. If anyone has any idea why the image is being corrupted, please let me know...
        I wrote the following program to test the copyResampled function in GD, and I have confirmed that the 'corruption' is really a bug in the GD library, most likely due to rounding errors (because of the resampling algorithm used) and I am getting an 'extra' line in both X and Y directions.

        use strict; use warnings; use IO::File; use GD; my $src = GD::Image->newFromJpeg('077.jpg'); my ($srcw, $srch) = $src->getBounds(); # (550, 413) my $destw = int ($srcw/3); my $desth = int ($srch/3); my $dest = GD::Image->new($destw-1, $desth-1); $dest->copyResampled($src,0,0,0,0,$destw,$desth,$srcw,$srch); my $f = new IO::File "078.jpg", "w" or die "Can not create image: $!"; binmode $f; print $f $dest->jpeg(80);

        To get around this problem, I just created the target canvas 1 pixel smaller in both X and Y directions. I was actually expecting the library to core dump on the resampling because the destination width and height are 1 pixel greater than the canvas, but the GD library has clipped the target bitmap properly. So excellent that's the fix.

        Another fix is to leave the target canvas unchanged, but add 1 to both destination width and height in the resample function, which also gives a good result.
        my $dest = GD::Image->new($destw, $desth); $dest->copyResampled($src,0,0,0,0,$destw+1,$desth+1,$srcw,$srch);