Problem with resizing PNG images in PHP

1

Good evening.

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

When uploading an image like this: .

Thescriptcheckstheextensionbasedonmimetype,"sanitizes" the file name and after that, generates 3 sizes of that image, such as Default, Thumbnail and Zoom. Being that in the "Standard" and "Thumbnail" images it resizes and in Zoom, 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":

Imageofthesize"Standard"

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

The script for uploading and handling this image is a bit complex, so I'll post here the snippets 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 loads the image and then resizes it:

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 set to 0, I already tried to leave it with 1, 5, and 9, all stayed the same.

"getFilters ()" is as "PNG_ALL_FILTERS".

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

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

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

Is there any way to work with PNG in PHP? Anything related to Alpha (I do not work too much with images, so I do not know what might be happening).

Thank you.

    
asked by anonymous 08.11.2015 / 05:10

2 answers

1

This answer delivers what you need and the quality you need, but you'll need to do a comparison and find out where the problem is. It is harder 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.

Note: The script below is the basics to answer your question, there are no checks or anything exceptional, it is functional to test with the image 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 want to test with images of other dimensions, change the variables $max_width and $max_height to the desired value.

[UPDATE]

Final Score

    
08.11.2015 / 23:30
0

Try passing the second parameter of imagealphablending to true :

imagealphablending($image, true);

If not, 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 .

    
08.11.2015 / 14:51