Problem with resizing PNG images in PHP

Good night.

I have a script from a virtual store here, but I'm having problems with PNG images.

When uploading an image like this: insert the description of the image here .

The script checks the extension based on the mimetype, "sanitizes" the file name and after all this, generates 3 sizes of this image, being Standard, thumbnail and Zoom. Being that in the "standard" and "thumbnail" images it does the resizing and zooming, if the image is large enough it keeps the same size.

The problem is that when it resizes to a smaller size, the image gets kind of "broken":

Size picture "Standard"

And in the thumbnail it gets even worse. How can I do to make this image smoother?

The script for uploading and processing this image is kind of complex, so I will post here the excerpts that I think are the main ones.

The script below uploads the image:

public static function importImage($temporaryPath, $originalFilename, $productId, $hash = false, $moveTemporaryFile = true, $generateImages = true)
{
    if (!file_exists($temporaryPath)) {
        throw new ISC_PRODUCT_IMAGE_SOURCEFILEDOESNTEXIST_EXCEPTION($temporaryPath);
    }

    try {
        $library = ISC_IMAGE_LIBRARY_FACTORY::getImageLibraryInstance($temporaryPath);
    } catch (ISC_IMAGE_LIBRARY_FACTORY_INVALIDIMAGEFILE_EXCEPTION $ex) {
        throw new ISC_PRODUCT_IMAGE_IMPORT_INVALIDIMAGEFILE_EXCEPTION();
    } catch (ISC_IMAGE_LIBRARY_FACTORY_NOPHPSUPPORT_EXCEPTION $ex) {
        throw new ISC_PRODUCT_IMAGE_IMPORT_NOPHPSUPPORT_EXCEPTION();
    }

    if ($library->getWidth() < 1 || $library->getHeight() < 1) {
        throw new ISC_PRODUCT_IMAGE_IMPORT_EMPTYIMAGE_EXCEPTION();
    }

    $finalName = $originalFilename;


    $finalName = basename($finalName); // remove any path components from the filename
    $finalName = self::sanitiseFilename($finalName);

    if (!self::isValidFilename($finalName, false)) {
        throw new ISC_PRODUCT_IMAGE_IMPORT_INVALIDFILENAME_EXCEPTION($finalName);
    }

    // correct the uploaded extension
    $correctExtension = $library->getImageTypeExtension(false);
    if (strtolower(pathinfo($finalName, PATHINFO_EXTENSION)) != $correctExtension) {
        // remove existing extension and trailing . if any
        $finalName = preg_replace('#\.[^\.]*$#', '', $finalName);
        // add correct extension
        $finalName .= '.' . $correctExtension;
    }

    // generate a path for storing in the product_images directory
    $finalRelativePath = self::generateSourceImageRelativeFilePath($finalName);

    $image = new ISC_PRODUCT_IMAGE();
    $image->setSourceFilePath($finalRelativePath);

    $finalAbsolutePath = $image->getAbsoluteSourceFilePath();
    $finalDirectory = dirname($finalAbsolutePath);

    if (!file_exists($finalDirectory)) {
        if (!isc_mkdir($finalDirectory, ISC_WRITEABLE_DIR_PERM, true)) {
            throw new ISC_PRODUCT_IMAGE_IMPORT_CANTCREATEDIR_EXCEPTION($finalDirectory);
        }
    }

    if ($moveTemporaryFile) {
        if (!@rename($temporaryPath, $finalAbsolutePath)) {
            throw new ISC_PRODUCT_IMAGE_IMPORT_CANTMOVEFILE_EXCEPTION($finalAbsolutePath);
        }
    } else {
        if (!@copy($temporaryPath, $finalAbsolutePath)) {
            throw new ISC_PRODUCT_IMAGE_IMPORT_CANTMOVEFILE_EXCEPTION($finalAbsolutePath);
        }
    }

    // check to see if the uploaded image exceeds our internal maximum image size: ISC_PRODUCT_IMAGE_MAXLONGEDGE
    if ($library->getWidth() > ISC_PRODUCT_IMAGE_MAXLONGEDGE || $library->getHeight() > ISC_PRODUCT_IMAGE_MAXLONGEDGE) {
        // if it is, resize it and overwrite the uploaded source image because we only want to store images to a maximum size of ISC_PRODUCT_IMAGE_MAXLONGEDGE x ISC_PRODUCT_IMAGE_MAXLONGEDGE
        $library->setFilePath($finalAbsolutePath);
        $library->loadImageFileToScratch();
        $library->resampleScratchToMaximumDimensions(ISC_PRODUCT_IMAGE_MAXLONGEDGE, ISC_PRODUCT_IMAGE_MAXLONGEDGE);
        $library->saveScratchToFile($finalAbsolutePath, self::getWriteOptionsForImageType($library->getImageType()));
    }

    if ($productId === false) {
        // do not assign product hash, id or save to database if $productId is false
        if ($generateImages) {
            // manually generate images since, normally, a call to saveToDatabase would do it
            $image->getResizedFileDimensions(ISC_PRODUCT_IMAGE_SIZE_TINY, true, false);
            $image->getResizedFileDimensions(ISC_PRODUCT_IMAGE_SIZE_THUMBNAIL, true, false);
            $image->getResizedFileDimensions(ISC_PRODUCT_IMAGE_SIZE_STANDARD, true, false);
            $image->getResizedFileDimensions(ISC_PRODUCT_IMAGE_SIZE_ZOOM, true, false);
        }

        return $image;
    }

    if ($hash) {
        $image->setProductHash($productId);
    } else {
        $image->setProductId($productId);
    }

    // ISC_PRODUCT_IMAGE_SOURCEFILEDOESNTEXIST_EXCEPTION should never really happen at this point with all the checks above so, if it does, let the exception go unhandled to bubble up to a fatal error
    $image->saveToDatabase($generateImages);

    return $image;
}

The below load the image and then resize:

public function loadImageFileToScratch()
{
    $filePath = $this->getFilePath();
    $imageType = $this->getImageType();

    // Attempt to increase the memory limit before loading in the image, to ensure it'll fit in memory
    ISC_IMAGE_LIBRARY_FACTORY::setImageFileMemLimit($filePath);

    switch ($imageType) {
        case IMAGETYPE_GIF:
            $this->_scratchResource = @imagecreatefromgif($filePath);
            if ($this->getScratchResource()) {
                imagecolortransparent($this->getScratchResource());
            }
            break;

        case IMAGETYPE_PNG:
            $this->_scratchResource = @imagecreatefrompng($filePath);
            if ($this->_scratchResource) {
                // this sets up alpha transparency support when manipulating and saving the in-memory image
                imagealphablending($this->getScratchResource(), false);
                imagesavealpha($this->getScratchResource(), true);
            }
            break;

        case IMAGETYPE_JPEG:
            $this->_scratchResource = @imagecreatefromjpeg($filePath);
            break;

        default:
            throw new ISC_IMAGE_LIBRARY_GD_UNSUPPORTEDIMAGETYPE_EXCEPTION($imageType);
    }

    $this->_updateImageInformation(true);

    if (!$this->getScratchResource()) {
        throw new ISC_IMAGE_LIBRARY_GD_IMAGECREATEFROMFILE_EXCEPTION($imageType, $filePath);
    }
}

After loading the image and making the necessary changes, the script below saves:

    public function saveScratchToFile($destinationFilePath, ISC_IMAGE_WRITEOPTIONS $imageWriteOptions)
{
    $imageType = $imageWriteOptions->getImageType();

    switch ($imageType) {

        case IMAGETYPE_JPEG:
            imagejpeg($this->getScratchResource(), $destinationFilePath, (int)$imageWriteOptions->getQuality());
            break;

        case IMAGETYPE_PNG:
            if (version_compare(PHP_VERSION, '5.1.3', '>=')) {
                // filters parameter was added in 5.1.3
                imagepng($this->getScratchResource(), $destinationFilePath, (int)$imageWriteOptions->getCompression(), (int)$imageWriteOptions->getFilters());
            } else if (version_compare(PHP_VERSION, '5.1.2', '>=')) {
                // quality parameter was added in 5.1.2
                imagepng($this->getScratchResource(), $destinationFilePath, (int)$imageWriteOptions->getCompression());
            } else {
                imagepng($this->getScratchResource(), $destinationFilePath);
            }
            break;

        case IMAGETYPE_GIF:
            imagegif($this->getScratchResource(), $destinationFilePath);
            break;

        default:
            throw new ISC_IMAGE_LIBRARY_GD_UNSUPPORTEDIMAGETYPE_EXCEPTION($imageType);
            break;
    }

    isc_chmod($destinationFilePath, ISC_WRITEABLE_FILE_PERM);
}

"getCompression ()" is like 0, i've tried to leave with 1, 5 and 9, they were all the same.

" getFilters () "is like"PNG_ALL_FILTERS".

PHP version is larger than 5.1.3, so it is using the first option.

I can't post the entire script here as it is multiple files for the whole upload/resize process, many of them are not even part of this problem. But if something is missing, just tell me that I look in the scripts and inform how it is.

This problem is only happening with PNG. JPEG and GIF are forming clean and smooth images, both in the "zoom", and in the "standard" and "thumbnail".

Is there a way to work with PNG in PHP? Something related to Alpha (I don't work much with images, so I don't know what can be happening).

Thank you.

Author: Junior Zancan, 2015-11-08

2 answers

This answer does what you need and with the quality you need, but you will need to perform a comparison and find where the problem is. It becomes more difficult to find the solution directly in your code because there are many external methods and you can not know all the values that are being passed and how they are being passed.

Obs.: The script below is the basic to answer your question, there are no checks or anything exceptional, it is functional to perform the test with the picture of your question.

<form action="" method="post" enctype="multipart/form-data">
    <input type="file" name="img" />
    <input type="submit" name="ok" value="Enviar" />
</form>

<?php
if($_POST['ok']) {
    $tempname = $_FILES["img"]["tmp_name"]; // Caminho completo da imagem original.
    $url = "img/nova_imagem.png"; // Caminho onde será salvo a nova imagem e nome do arquivo.
    $max_width = 300; // Largura final da imagem.
    $max_height = 225; // Altura final da imagem.

    move_uploaded_file($tempname, $url); // Move arquivo para servidor.

    // Pega a largura, altura, tipo e atributo da imagem
    list($image_width, $image_height, $type, $attribute) = getimagesize($url);

    // Testa se é preciso redimensionar a imagem
    if(($image_width > $max_width) || ($image_height > $max_height)) {

        if($image_width > $image_height) {
            $new_height = round(($max_width / $image_width) * $image_height);
            $new_width = $max_width;
        }

        if($image_height > $image_width) {
            $new_width = round(($max_height / $image_height) * $image_width);
            $new_height = $max_height;
        }

        if($image_width == $image_height) {
            if($max_width > $max_height) {
                $new_width = round($max_width - ($max_width - $max_height));
                $new_height = $max_height;
            }
            if($max_height > $max_width) {
                $new_height = round($max_height - ($max_height - $max_width));
                $new_width = $max_width;
            }

            if($max_height == $max_width) {
                $new_height = $max_height;
                $new_width = $max_width;
            }
        }

        // Cria uma nova imagem com o novo tamanho  
        $new_image = imagecreatetruecolor($new_width, $new_height);

        // Define o background desta nova imagem (RGB), neste caso, 'transparente' como precisa.
        $transparent = imagecolorallocatealpha($new_image, 0, 0, 0, 127);
        imagefill($new_image, 0, 0, $transparent);
        imagealphablending($new_image, true);
        imagesavealpha($new_image, true);

        $source = imagecreatefrompng($url);
                  imagecopyresampled($new_image, $source, 0, 0, 0, 0,   $new_width, $new_height, $image_width, $image_height);
                  imagepng($new_image, $url, 0); // Compactação '0'

        // Destrói as imagens criadas
        imagedestroy($new_image);
        imagedestroy($source);
    }
}
?>

If you need to test with images of other dimensions, change the variables $max_width and $max_height to the desired value.

[UPDATE]

Final Result

Final Result

 1
Author: Thyago ThySofT, 2015-11-08 22:30:01

Try passing the second parameter of imagealphablending as true:

imagealphablending($image, true);

Otherwise, try to create a transparent color and fill the image with that color, before copying it:

$transparent = imagecolorallocatealpha($image, 0, 0, 0, 127);
imagefill($image, 0, 0, $transparent);
imagealphablending($image, true);

I Saw in this Stack Overflow response in English.

 0
Author: Rodrigo Rigotti, 2017-05-23 12:37:30