Hey everyone,
I have a script that I use to upload images. It is used when I upload for example a post image or a profile image, and I use it to create the thumbnails for those images also.
The script itself is working fine, but, the problem I am having is that the resulting image quality is very low compared to what I am actually uploading.
The function accepts the file src, the destination where it should be saved in, and the width and height I want to save it in.
Here is the code:
public function create($srcFile, $dest, $targetWidth, $targetHeight = null)
{
/**
* Get the image type from the source image.
* This will be used to validate that this is an image, and that its an
* image this class supports by checking id it exists in the array
* of image options.
*/
$srcType = exif_imagetype($srcFile);
/**
* If this file is not a valid image, or, the image type is not in the
* array of image options, return false.
*/
try
{
if (!$srcType || !isset($this->imageOptions[$srcType])) {
throw new SilentException('Upload Image Failed: '.$srcType.' not supported');
}
}catch(SilentException $e){
return false;
}
/**
* Create an image identifier based on the image type. This script will
* use the image options array to get the correct function to create
* the image identifier.
*/
$image = call_user_func($this->imageOptions[$srcType]['load'], $srcFile);
/**
* If the image identifier wasnt created, return false.
*/
try
{
if (!$image) {
throw new SilentException('Upload Image Failed: Image identifier wasnt created');
}
}catch(SilentException $e){
return false;
}
/**
* Get the width and the height of the original image.
*/
$origWidth = imagesx($image);
$origHeight = imagesy($image);
/**
* If the target height was not set, set it to be the same height
* as the original image.
*/
$targetHeight = $targetHeight ? $targetHeight : $origHeight;
/**
* Get the aspect ratios of both source image and the target
*/
$oldAspRatio = $origWidth / $origHeight; //EX: 20 / 2 = 10
$newAspRatio = $targetWidth / $targetHeight; //EX: 10 / 2 = 2
/**
* If the source image is wider than the target image, keep the same
* height and calculate the width it should have.
*/
if($oldAspRatio >= $newAspRatio){
$newHeight = $targetHeight;
$newWidth = round($origWidth / ($origHeight / $targetHeight));
/**
* If the source image is narrower than the target image, keep the same
* width and calculate the height it should have.
*/
}else{
$newWidth = $targetWidth;
$newHeight = round($origHeight / ($origWidth / $targetWidth));
}
/**
* Set up the position the image should have based on the ratio of the
* image. It will basically center the image vertically and horizontally
* in the thumbnail once its created.
*/
$destX = round(0 - ($newWidth - $targetWidth) / 2);
$destY = round(0 - ($newHeight - $targetHeight) / 2);
/**
* Create the thumbnail image placeholder
*/
$thumbnail = imagecreatetruecolor($targetWidth,$targetHeight);
/**
* Set transparency options for GIFs and PNGs
*/
if ($srcType == IMAGETYPE_GIF || $srcType == IMAGETYPE_PNG) {
//Make the image placeholder transparent
imagecolortransparent(
$thumbnail,
imagecolorallocate($thumbnail, 0, 0, 0)
);
//Additional settings for PNGs
if ($srcType == IMAGETYPE_PNG) {
imagealphablending($thumbnail, false);
imagesavealpha($thumbnail, true);
}
}
/**
* Copy the source image to the image placeholder and resize it
*/
imagecopyresampled(
$thumbnail,
$image,
$destX, $destY, 0, 0,
$newWidth, $newHeight,
$origWidth,
$origHeight
);
/**
* Determine how to save the thumbnail.
* This will check the destination filenames extension, and use the
* correct saving function for it.
*/
$saveExt = pathinfo($dest, PATHINFO_EXTENSION);
switch (strtolower($saveExt)) {
case 'png':
$saveType = IMAGETYPE_PNG;
break;
case 'jpg':
case 'jpeg':
$saveType = IMAGETYPE_JPEG;
break;
case 'gif':
$saveType = IMAGETYPE_GIF;
break;
default:
$saveType = IMAGETYPE_PNG;
break;
}
/**
* Save the thumbnail and return the result of it
*/
//Create a webp format of the file
$webPName = str_replace('.png', '.webp', $dest);
imagewebp($thumbnail, $webPName, 100);
return call_user_func(
$this->imageOptions[$saveType]['save'],
$thumbnail,
$dest,
$this->imageOptions[$saveType]['quality']
);
}
Here is where I set the quality (it’s always set to use the heighest quality):
private $imageOptions = [
IMAGETYPE_JPEG => [
'load' => 'imagecreatefromjpeg',
'save' => 'imagejpeg',
'quality' => 100
],
IMAGETYPE_PNG => [
'load' => 'imagecreatefrompng',
'save' => 'imagepng',
'quality' => 0
],
IMAGETYPE_GIF => [
'load' => 'imagecreatefromgif',
'save' => 'imagegif',
'quality' => 100
],
IMAGETYPE_WEBP => [
'load' => 'imagecreatefromwebp',
'save' => 'imagewebp',
'quality' => 100
]
];
With this function, I save all of the images as PNG files, and I also create webp versions of the images. Both have the same low quality in comparison to the uploaded image.
I have tried uploading images that had the same dimensions as they would have after they are saved, and I have tried uploading larger images, anywhere from 2-4x the dimensions the function actually saves in, but they always come out the same.
To clarify, if the goal is to save an image at 100x100 and I upload a high quality image that is 100x100, it comes out blurry. If I upload an image that is 200x200, and save it at 100x100, it’s still blurry. If I go even larger than that, same result.
Here is an example of an image that I upload:
And here is the image after it is uploaded and saved:
The original image is much sharper and has finer details, the actual uploaded result is just much more blurry.
Any thoughts?
Thanks!