PHP GD library sacing lowe quality images

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!

I may have missed it, but where do you set the quality of the images?

Oh, yes, sorry, there is also this peice of code for the 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
    ]
];

It’s always set to use the heighest quality.

The two examples are different in size. So its clear that the second image has not the same sharpness as the first one. It contains 1/4 of the information…

Can you show us an example where both images have the same size?

This is before:

This is after:

But this is strange now though, because when I download the image, it actually looks much smaller, and higher quality, but the dimensions are the same, but when I upload even to here, it looks bigger and lower quality.
This is how it looks on my computer:

I had no idea about this, but I just read (and tested) that the inconsistancy between the image on my desktop vs the image in the browser is becauase my screens DPI is scaled up, one screen is to 125% and the other to 150%. This makes the browsers that much bigger, but not the windows 11 app that opens images. So the images on desktop are smaller in appearance, and high quality, and when viewing them in the browser they are scaled up and blurry.

I wonder what the solution is for this, since even when I upload to sitepoint, the image comes out blurry, but when I look at other images on the web, they look fine.

Maybe it’s jsut to upload larger images and scale them down in output?

The easiest way is to load the image into a paint Programm. Then zoom in, until you see each pixel. So you can compare images objectively. All other views are subjectively and do not help anyone.