Php - replace base color of transparent png image

前端 未结 6 2022
栀梦
栀梦 2020-12-28 09:35

I have searched a lot and I found only few solutions (on google and stackoverflow so please don\'t mark this one as a duplicate unless there\'s really duplicate question), b

相关标签:
6条回答
  • 2020-12-28 09:52

    The third image doesn't look fine, because imagetruecolortopalette($im,true, 255); renders an ugly image:

    enter image description here

    Since the second image doesn't look fine, the third can't look beautiful too.

    Code:

    <?php
    unlink( dirname ( __FILE__ ) . '/newleaf.png' );
    unlink( dirname ( __FILE__ ) . '/newleaf1.png' );
    
    function LoadPNG( $imgname )
    {
        $im = imagecreatefrompng ($imgname);
        imagetruecolortopalette($im,true, 255);
    
        imagepng($im, 'newleaf1.png' ); // save image as png
    
        $index = imagecolorclosest ( $im,  0,0,0 ); // GET BLACK COLOR
        imagecolorset($im,$index,0,150,255); // SET COLOR TO BLUE
        $name = basename($imgname);
        imagepng($im, 'newleaf.png' ); // save image as png
        imagedestroy($im);
    }
    
    $img = dirname ( __FILE__ ) . '/leaf.png';
    LoadPNG( $img );
    
    ?>
    
    Original
    <img src="leaf.png">
    <br />After make truecolortopalette($im,true, 255);
    <img src="newleaf1.png">
    <br />Thus..
    <img src="newleaf.png">
    
    0 讨论(0)
  • 2020-12-28 09:54

    This code doesn't exemplify the problem, but transforms colors like this:

    enter image description here

    Uses the ALPHA channel of an image to determines coloring. For other results, just play around with imagecolorallocatealpha():

    function colorizeBasedOnAplhaChannnel( $file, $targetR, $targetG, $targetB, $targetName ) {
    
        $im_src = imagecreatefrompng( $file );
    
        $width = imagesx($im_src);
        $height = imagesy($im_src);
    
        $im_dst = imagecreatefrompng( $file );
    
        // Note this:
        // Let's reduce the number of colors in the image to ONE
        imagefilledrectangle( $im_dst, 0, 0, $width, $height, 0xFFFFFF );
    
        for( $x=0; $x<$width; $x++ ) {
            for( $y=0; $y<$height; $y++ ) {
    
                $alpha = ( imagecolorat( $im_src, $x, $y ) >> 24 & 0xFF );
    
                $col = imagecolorallocatealpha( $im_dst,
                    $targetR - (int) ( 1.0 / 255.0  * $alpha * (double) $targetR ),
                    $targetG - (int) ( 1.0 / 255.0  * $alpha * (double) $targetG ),
                    $targetB - (int) ( 1.0 / 255.0  * $alpha * (double) $targetB ),
                    $alpha
                    );
    
                if ( false === $col ) {
                    die( 'sorry, out of colors...' );
                }
    
                imagesetpixel( $im_dst, $x, $y, $col );
    
            }
    
        }
    
        imagepng( $im_dst, $targetName);
        imagedestroy($im_dst);
    
    }
    
    unlink( dirname ( __FILE__ ) . '/newleaf.png' );
    unlink( dirname ( __FILE__ ) . '/newleaf1.png' );
    unlink( dirname ( __FILE__ ) . '/newleaf2.png' );
    
    $img = dirname ( __FILE__ ) . '/leaf.png';
    colorizeBasedOnAplhaChannnel( $img, 0, 0, 0xFF, 'newleaf1.png' );
    colorizeBasedOnAplhaChannnel( $img, 0xFF, 0, 0xFF, 'newleaf2.png' );
    ?>
    
    Original
    <img src="leaf.png">
    <br />
    <img src="newleaf1.png">
    <br />
    <img src="newleaf2.png">
    
    0 讨论(0)
  • 2020-12-28 09:54

    Using SteAp's accepted code as a starting point (since with it i didnt manage to achieve transparency, just a white background), i adapted said code and the result is this:

    <?php 
    
    function colorizeKeepAplhaChannnel( $inputFilePathIn, $targetRedIn, $targetGreenIn, $targetBlueIn, $outputFilePathIn ) {
        $im_src = imagecreatefrompng( $inputFilePathIn );
        $im_dst = imagecreatefrompng( $inputFilePathIn );
        $width = imagesx($im_src);
        $height = imagesy($im_src);
    
        // Note this: FILL IMAGE WITH TRANSPARENT BG
        imagefill($im_dst, 0, 0, IMG_COLOR_TRANSPARENT);
        imagesavealpha($im_dst,true);
        imagealphablending($im_dst, true);
    
        $flagOK = 1;
        for( $x=0; $x<$width; $x++ ) {
            for( $y=0; $y<$height; $y++ ) {
                $rgb = imagecolorat( $im_src, $x, $y );
                $colorOldRGB = imagecolorsforindex($im_src, $rgb);
                $alpha = $colorOldRGB["alpha"];
                $colorNew = imagecolorallocatealpha($im_src, $targetRedIn, $targetGreenIn, $targetBlueIn, $alpha);
    
                $flagFoundColor = true;
                // uncomment next 3 lines to substitute only 1 color (in this case, BLACK/greys)
    /*
                $colorOld = imagecolorallocatealpha($im_src, $colorOldRGB["red"], $colorOldRGB["green"], $colorOldRGB["blue"], 0); // original color WITHOUT alpha channel
                $color2Change = imagecolorallocatealpha($im_src, 0, 0, 0, 0); // opaque BLACK - change to desired color
                $flagFoundColor = ($color2Change == $colorOld);
    */
    
                if ( false === $colorNew ) {
                    //echo( "FALSE COLOR:$colorNew alpha:$alpha<br/>" );
                    $flagOK = 0; 
                } else if ($flagFoundColor) {
                    imagesetpixel( $im_dst, $x, $y, $colorNew );
                    //echo "x:$x y:$y col=$colorNew alpha:$alpha<br/>";
                } 
            }
        }
        $flagOK2 = imagepng($im_dst, $outputFilePathIn);
    
        if ($flagOK && $flagOK2) {
            echo ("<strong>Congratulations, your conversion was successful </strong><br/>new file $outputFilePathIn<br/>");
        } else if ($flagOK2 && !$flagOK) {
            echo ("<strong>ERROR, your conversion was UNsuccessful</strong><br/>Please verify if your PNG is truecolor<br/>input file $inputFilePathIn<br/>");
        } else if (!$flagOK2 && $flagOK) {
            $dirNameOutput = dirname($outputFilePathIn)."/";
            echo ("<strong>ERROR, your conversion was successful, but could not save file</strong><br/>Please verify that you have PERMISSION to save to directory $dirName <br/>input file $inputFilePathIn<br/>");
        } else {
            $dirNameOutput = dirname($outputFilePathIn)."/";
            echo ("<strong>ERROR, your conversion was UNsuccessful AND could not save file</strong><br/>Please verify if your PNG is truecolor<br/>Please verify that you have PERMISSION to save to directory $dirName <br/>input file $inputFilePathIn<br/>");
        }
    
        echo ("TargetName:$outputFilePathIn wid:$width height:$height CONVERTED:|$flagOK| SAVED:|$flagOK2|<br/>");
        imagedestroy($im_dst);
        imagedestroy($im_src);
    }
    
    
    
    
    $targetRed = 0;
    $targetGreen = 180;
    $targetBlue = 0;
    
    //$inputFileName = 'frameSquareBlack_88x110.png';
    $inputFileName = 'testMe.png';
    $dirName = "../img/profilePics/";
    $nameTemp = basename($inputFileName, ".png");
    $outputFileName = $nameTemp."_$targetRed"."_$targetGreen"."_$targetBlue.png";
    $inputFilePath = $dirName.$inputFileName;
    $outputFilePath = $dirName.$outputFileName;
    
    //echo "inputFileName:$inputFilePath<br>outputName:$outputFilePath<br>";
    colorizeKeepAplhaChannnel( $inputFilePath, $targetRed, $targetGreen, $targetBlue, $outputFilePath);
    ?>
    <br/><br/>
    Original <br/>
    <img src="<?php echo $inputFilePath; ?>">
    <br /><br />Colorized<br/>
    <img src="<?php echo $outputFilePath; ?>">
    <br />
    

    enter image description here

    this variation changes ALL colors to chosen color (not just black, a simple IF can solve the problem - uncomment 3 indicated lines in function and you will achieve this)

    enter image description here

    For illustrative purposes, in this case, the following image was used (because leaf.png is monochromatic, with transparency): enter image description here

    0 讨论(0)
  • 2020-12-28 10:03

    As I already told, I spent a lot of time searching and what I found so far is using html5 canvas, javascript and ajax.

    Only library I used is javascript library jQuery but it is optional. Code can be easily rewritten to use plain javascript.

    How it works:

    1) js pulls data from ajax.php which returns an array of all the files

    2) js then loops thru file list and performs change(src,color) for each item

    3) js function change(src,color) loads image from source, replaces it's color and adds an img element to #Cell and displays it (for debug).

    4) change() also calls save(src,filename,cname) function 5) js function save(src,filename,cname) sends an ajax request with image data and ajax.php saves image to server.

    So here's the code:

    ajax.php

    <?php
    $r = $_REQUEST;
    $act = $r['action'];
    if($act == "get_all") {
        $js = "";
        $dir = getcwd()."/img/";
        $images = glob($dir."/*.png",GLOB_BRACE);
        foreach($images as $image) {
            $name = basename($image);
            $js[] = $name;
        }
        echo json_encode($js);
        die();
    }
    elseif($act == "save") {
        $img = $r['file'];
        $name = $r['name'];
        $color = $r['color'];
        $dir = "results/$color";
        if(!file_exists($dir) || !is_dir($dir)) mkdir($dir,777,true);
        $file = $dir."/$name";
        file_put_contents($file,file_get_contents("data://".$img));
        if(file_exists($file)) echo "Success";
        else echo $file;
        die();
    }
    

    index.php (html only)

    <!doctype html>
            <html>
    <head>
        <script src="jquery.js" type="text/javascript"></script>
        <script src="demo.js" type="text/javascript"></script>
    </head>
    <body>
    <div id="ctrl">
        <input type="text" id="color" value="#666666" placeholder="Color in HEX format (ex. #ff0000)" />
        <input type="text" id="cname" value="grey" placeholder="Color name (destionation dir name)" />
        <button type="button" id="doit">Change</button>
    </div>
    <div id="Cell">
    
    </div>
    </body>
    
    </html>
    

    demo.js

    $(document).ready(function() {
        $(document).on("click","#doit",function() {
            var c = $("#color");
            if(c.val() != "") {
                $("#Cell").html("");
                $.post("ajax.php",{ action: "get_all" },function(s) {
                    var images = $.parseJSON(s);
                    $.each(images, function(index, element) {
                        change(images[index], c.val());
                    });
                });
            }
        });
    });
    function change(src,color) {
        var myImg = new Image();
        myImg.src = "img/"+src;
        myImg.onload = function() {
            var canvas = document.createElement("canvas");
            var ctx = canvas.getContext("2d");
            ctx.drawImage(myImg,0,0);
            var imgd = ctx.getImageData(0, 0, myImg.width, myImg.height);
            canvas.height = myImg.height;
            canvas.width = myImg.width;
            var new_color = HexToRGB(color);
            // console.log(imgd)
            for (i = 0; i <imgd.data.length; i += 4) {
                imgd.data[i]   = new_color.R;
                imgd.data[i+1] = new_color.G;
                imgd.data[i+2] = new_color.B;
            }
            ctx.putImageData(imgd, 0, 0);
            var newImage=new Image()
            newImage.src=canvas.toDataURL("image/png");
            $(newImage).css("margin","5px");
            $(newImage).attr('data-title',src);
            $("#Cell").append(newImage);
            var c = $("#cname");
            if(c.val() == "") c.val("temp");
            save(newImage.src,src, c.val());
        };
    }
    function save(src,filename,cname) {
        $.post("ajax.php", { action: "save", file: src, name: filename, color: cname },function(s) {
            console.log(s);
        })
    }
    function HexToRGB(Hex)
    {
        var Long = parseInt(Hex.replace(/^#/, ""), 16);
        return {
            R: (Long >>> 16) & 0xff,
            G: (Long >>> 8) & 0xff,
            B: Long & 0xff
        };
    }
    

    I have tested it, for re-coloring and saving 420 24x24 images, it took less than 10 seconds (on localhost) (420 async ajax calls). Once original images are cached, it finishes much faster. Image quality stays the same as original.

    Again, this solution is for my personal use so code is pretty unmanaged and I am sure it can be improved but here you go - as is, it works.

    0 讨论(0)
  • 2020-12-28 10:11

    Extending on the answer from SteAp, I had the need to also be able to adjust the transparency of each pixel based on an RGBA target color.

    I also fixed the issue of having dark edges - which was a result of each pixel's color being adjusted by the original alpha level, rather than just adjusting the alpha on it's own.

    // R,G,B = 0-255 range
    // A = 0.0 to 1.0 range
    function colorizeBasedOnAplhaChannnel($file, $targetR, $targetG, $targetB, $targetA, $targetName ) {
        $im_src = imagecreatefrompng($file);
    
        $width = imagesx($im_src);
        $height = imagesy($im_src);
    
        $im_dst = imagecreatefrompng($file);
    
        // Turn off alpha blending and set alpha flag
        imagealphablending($im_dst, false);
        imagesavealpha($im_dst, true);
    
        // Fill transparent first (otherwise would result in black background)
        imagefill($im_dst, 0, 0, imagecolorallocatealpha($im_dst, 0, 0, 0, 127));
    
        for ($x=0; $x<$width; $x++) {
            for ($y=0; $y<$height; $y++) {
                $alpha = (imagecolorat( $im_src, $x, $y ) >> 24 & 0xFF);
    
                $col = imagecolorallocatealpha( $im_dst,
                    $targetR - (int) ( 1.0 / 255.0 * (double) $targetR ),
                    $targetG - (int) ( 1.0 / 255.0 * (double) $targetG ),
                    $targetB - (int) ( 1.0 / 255.0 * (double) $targetB ),
                    (($alpha - 127) * $targetA) + 127
                );
    
                if (false === $col) {
                    die( 'sorry, out of colors...' );
                }
    
                imagesetpixel( $im_dst, $x, $y, $col );
            }
        }
    
        imagepng( $im_dst, $targetName);
        imagedestroy($im_dst);
    }
    
    0 讨论(0)
  • 2020-12-28 10:14

    I have tried example from SteAp, but it's not working on some files. I use imagemagick instead:

    convert liquid.png -fuzz 100% -fill 'green' +opaque transparent -colorize 100 liquid_im.png
    
    0 讨论(0)
提交回复
热议问题