Sunday, July 17, 2022

[FIXED] How to create the smallest possible transparent png/gif of a given size in PHP?

Issue

I've made this code to generate the data-uri for a transparent PNG of a given size:

function createTransparentDataURI($w = 1, $h = 1) {

    // Enable output buffering
    ob_start();

    $img = imagecreatetruecolor($w, $h);
    imagesavealpha($img, true);
    $color = imagecolorallocatealpha($img, 0, 0, 0, 127);
    imagefill($img, 0, 0, $color);
    imagepng($img);
    imagedestroy($img);

    // Capture the output
    $imagedata = ob_get_contents();
    // Clear the output buffer
    ob_end_clean();

    // REF: http://stackoverflow.com/questions/9370847/php-create-image-with-imagepng-and-convert-with-base64-encode-in-a-single-file
    return 'data:image/png;base64,' . base64_encode($imagedata);
}

An example run with

echo createTransparentDataURI(1016, 312);

returns

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA/gAAAE4CAYAAADvrFgKAAAE40lEQVR4nO3BAQ0AAADCoPdPbQ8HFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwZltVAAEv7IggAAAAAElFTkSuQmCC

Is there any way to "compress" (PNG is lossless, but with a compression option?) this data-uri with a better encoding or using a single-color GIF? Programmatic graphics with PHP isn't my strongest area.


Solution

Considering that the image will be transparent and nothing will appear on it you don't need it to be lossless. This means you can use a color palette of single color which will create a index for this color and use the index for all pixels in the image. To create index palette PNG you should use the imagetruecolortopalette() function in your code: http://php.net/manual/en/function.imagetruecolortopalette.php

You can also set the image compression level to highest.

function createTransparentDataURI($w = 1, $h = 1) {
    //...
    //create image palette with one color, the dithering (the second argument) doesn't matter here
    imagetruecolortopalette($img, false, 1);
    imagepng($img, null, 9); //set the compression level to highest
    //...
}

This reduced the data length of image 1016x312 from 1308 to 133 bytes, which is almost by factor of ten.

By converting the binary data to base64 you can see with naked eye that there is room to compress that too:

iVBORw0KGgoAAAANSUhEUgAAA/gAAAE4AQMAAADVYspJAAAAA1BMVEUEAgSVKDOdAAAAPUlEQVR42u3BAQ0AAADCoPdPbQ8HFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/BicAAABWZX81AAAAABJRU5ErkJggg==

where the multiple "A"s are

You can enable the http's server gzip compression, to compress this response further more.

GIF wouldn't do better as it's size in my test was 910 bytes.



Answered By - Pavel Petrov
Answer Checked By - Dawn Plyler (PHPFixing Volunteer)

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.