Search code examples
phpimagepngimage-manipulationlinear-gradients

Gradient overlay on transparent png


On my website I have a PHP script that turns this image into a recolored version of the same image based on a $_GET query. An example with the query being 07a is available here.

Here is the PHP script that does this:

<?php

    if (isset($_GET['color']) && $_GET['color'] !== "") $color = preg_match('/^([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/',$_GET['color']) ? $_GET['color'] : '777777';
    else $color = '000000';

    if (strlen($color) === 3) $color = $color[0].$color[0].$color[1].$color[1].$color[2].$color[2];

    $color = str_split($color,2);
    foreach ($color as $k=>$m){ $color[$k] = intval($color[$k],16); }

    $img = imageCreateFromPng('splat-base.png');
    $background = imagecolorallocate($img, 0, 0, 0);
    imagecolortransparent($img, $background);
    imagealphablending($img, false);
    imagesavealpha($img, true);

    imagefilter($img, IMG_FILTER_COLORIZE, intval($color[0] - 255 ,16), $color[1], $color[2]);

    header('Content-type: image/png');
    imagePng($img);
    imagedestroy($img);
    exit;
?>

The actual work is done by the imagefilter function, the rest is just for preserving transparency. What I would like to do, is to have 2 color values sent via $_GET, and create a top-to-bottom gradient applied to the image with the given color values. Here is an example with the values being 08f and 0a7 (lighter color will always be first):

gradient splat

One possible solution I thought of was to generate an image of the same size as my splat-base.png (1000 x 1000 px) with the gradient, then with some trickery apply that over the splat image, but I don't know how to go about generating that gradient image or how to apply it to the splat image either.

I don't have the ability to install PHP extensions like ImageMagick, so I must work with the built in functions of PHP and libraries that only use those.


Solution

  • So, I googled how to create an image gradient in php. The idea is that, you define the start and end color, and define a step size of $stepSize = ($end-$start)/($imgHeight). You make a for loop, and in each step of the loop, create a 1 pixel tall rectangle that is as wide the image, and set its color to $start+$i*$stepSize. That idea, and code, I found here:

    http://www.geekthis.net/blog/87/php-gradient-images-rectangle-gd

    That code creates a new image of a linear gradient. For your situation, I ran with that basic idea: Shave off 1 pixel rows from your source image, and IMG_FILTER_COLORIZE each row to the color of the gradient.

    Here is the code:

    <?php
    
    //move alpha preserving code to its own function 
    //since it is used several times
    function preserveAlpha(&$img) {
        $background = imagecolorallocate($img, 0, 0, 0);
        imagecolortransparent($img, $background);
        imagealphablending($img, false);
        imagesavealpha($img, true);
    }
    
    $img = imagecreatefrompng('splat-base.png');
    preserveAlpha($img);
    //grab width and height
    $width = imagesx($img);
    $height = imagesy($img);
    
    //the final image
    $final = imagecreatetruecolor($width, $height);
    
    preserveAlpha($final);
    
    //start and end values of the gradient
    $start = $_GET['start'];
    $end = $_GET['end'];
    
    //convert 3 color codes to 6
    if (strlen($start) == 3) {
        $start = $start[0] . $start[0] . $start[1] . $start[1] . $start[2] . $start[2];
    }
    
    if (strlen($end) == 3) {
        $end = $end[0] . $end[0] . $end[1] . $end[1] . $end[2] . $end[2];
    }
    
    
    //make sure the lighter color is first
    
    $starthex = intval($start, 16);
    $endhex = intval($end, 16);
    if ($endhex > $starthex) {
        //then we need to flip
        $temp = $end;
        $end = $start;
        $start = $temp;
    }
    
    //convert from hex to rgb
    $s = array(
        hexdec(substr($start, 0, 2)),
        hexdec(substr($start, 2, 2)),
        hexdec(substr($start, 4, 2))
    );
    $e = array(
        hexdec(substr($end, 0, 2)),
        hexdec(substr($end, 2, 2)),
        hexdec(substr($end, 4, 2))
    );
    
    //the for loop. go through each 1 pixel row of the image, 
    //shave it off, apply a filter, and append it to our new image
    
    for ($i = 0; $i < $height; $i++) {
        //grab the $ith row off of the image
        $pixelRow = imagecreatetruecolor($width, 1);
        preserveAlpha($pixelRow);
        imagecopy($pixelRow, $img, 0, 0, 0, $i, $width, 1);
        //calculate the next color in the gradient
        $r = intval($s[0] - ((($s[0] - $e[0]) / $height) * $i));
        $g = intval($s[1] - ((($s[1] - $e[1]) / $height) * $i));
        $b = intval($s[2] - ((($s[2] - $e[2]) / $height) * $i));
        //apply the filter
        imagefilter($pixelRow, IMG_FILTER_COLORIZE, $r - 255, $g, $b);
        //append it to our new iamge
        imagecopy($final, $pixelRow, 0, $i, 0, 0, $width, 1);
    }
    
    header('Content-Type: image/png');
    imagepng($final);
    
    
    /* Some Cleanup */
    imagedestroy($final);
    
    exit;
    ?>