Czy można zachować przezroczystość obrazu PNG, używając biblioteki PHP imagecopyresampled z GDlib?

101

Poniższy fragment kodu PHP używa GD do zmiany rozmiaru pliku PNG przesłanego do przeglądarki na 128x128. Działa świetnie, poza tym, że przezroczyste obszary na oryginalnym obrazie są zastępowane jednolitym kolorem - w moim przypadku czarnym.

Chociaż imagesavealphajest ustawione, coś jest nie tak.

Jaki jest najlepszy sposób na zachowanie przezroczystości ponownie próbkowanego obrazu?

$uploadTempFile = $myField[ 'tmp_name' ]
list( $uploadWidth, $uploadHeight, $uploadType ) 
  = getimagesize( $uploadTempFile );

$srcImage = imagecreatefrompng( $uploadTempFile );    
imagesavealpha( $targetImage, true );

$targetImage = imagecreatetruecolor( 128, 128 );
imagecopyresampled( $targetImage, $srcImage, 
                    0, 0, 
                    0, 0, 
                    128, 128, 
                    $uploadWidth, $uploadHeight );

imagepng(  $targetImage, 'out.png', 9 );
Cheekysoft
źródło

Odpowiedzi:

200
imagealphablending( $targetImage, false );
imagesavealpha( $targetImage, true );

zrobił to dla mnie. Dzięki, ceejayoz.

Zwróć uwagę, że obraz docelowy wymaga ustawień alfa, a nie obraz źródłowy.

Edycja: pełny kod zastępczy. Zobacz także odpowiedzi poniżej i ich komentarze. Nie ma gwarancji, że będzie to w żaden sposób doskonałe, ale spełniło moje ówczesne potrzeby.

$uploadTempFile = $myField[ 'tmp_name' ]
list( $uploadWidth, $uploadHeight, $uploadType ) 
  = getimagesize( $uploadTempFile );

$srcImage = imagecreatefrompng( $uploadTempFile ); 

$targetImage = imagecreatetruecolor( 128, 128 );   
imagealphablending( $targetImage, false );
imagesavealpha( $targetImage, true );

imagecopyresampled( $targetImage, $srcImage, 
                    0, 0, 
                    0, 0, 
                    128, 128, 
                    $uploadWidth, $uploadHeight );

imagepng(  $targetImage, 'out.png', 9 );
Cheekysoft
źródło
5
FIY, musi to nastąpić po utworzeniu obrazu docelowego. W tym przypadku byłoby to po imageecreatetruecolor.
Rising Sun
Zastanawiasz się, dlaczego jest alphablending = falsetu ważne? Dokument stwierdza, że ... „Aby z niego skorzystać, musisz wyłączyć ustawienie alphablending ( imagealphablending($im, false))”.
osiemdziesiąt pięć
2
Ta odpowiedź jest nie tylko poprawna i użyteczna, ale jest szczególnie pomocna, ponieważ pierwszy komentarz (w momencie pisania) do dokumentacji PHP dla imagecreatefrompng()sugeruje, że imagealphablendingnależy ją ustawić true, co jest ewidentnie błędne. Dziękuję bardzo.
spikyjt
3
To rozwiązanie, w moim przypadku GD działa dobrze TYLKO jeśli PNG ma "zwykły" obszar przezroczystości, jak otaczający obszar przezroczysty, jeśli ma złożony obszar, jak na przykład z wewnętrznymi częściami obrazu z przezroczystością, zawsze zawodzi i nakłada czarne tło , na przykład ten obraz nie działa: seomofo.com/downloads/new-google-logo-knockoff.png . Czy ktoś może to spróbować i potwierdzić?
aesede
2
Wydaje się, że nie działa z niektórymi przezroczystymi plikami png. Próbowałem stworzyć obraz z jpg i skopiować do środka przezroczysty png. Jak zauważa Aesede, wynikiem jest czarny kwadrat.
RubbelDeCatc
21

Dlaczego tak komplikujesz sprawy? Oto, czego używam i jak dotąd spełniło to za mnie zadanie.

$im = ImageCreateFromPNG($source);
$new_im = imagecreatetruecolor($new_size[0],$new_size[1]);
imagecolortransparent($new_im, imagecolorallocate($new_im, 0, 0, 0));
imagecopyresampled($new_im,$im,0,0,0,0,$new_size[0],$new_size[1],$size[0],$size[1]);

źródło
Nie zadziałało, nadal mam czarne tło z tym obrazem: seomofo.com/downloads/new-google-logo-knockoff.png
aesede
Wypróbowywałem oba rozwiązania: Twoje i powyższe z ponad 150 głosami. Twoje rozwiązania świetnie sprawdzają się w przypadku plików GIF. Powyższy działa najlepiej z plikami PNG, podczas gdy Twoje rozwiązanie traci wygładzanie, co widać najlepiej podczas tworzenia miniatur (wygląda na blokowe, pikselowane).
Jonny
11

Uważam, że to powinno załatwić sprawę:

$srcImage = imagecreatefrompng($uploadTempFile);
imagealphablending($srcImage, false);
imagesavealpha($srcImage, true);

edycja: Ktoś w oświadczeniach PHP docs imagealphablendingpowinien być prawdziwy, a nie fałszywy. YMMV.

ceejayoz
źródło
2
Używając imagealphablendingzarówno wartości true, jak i false, zawsze mam czarne tło.
aesede
PHP7 - Praca dla mnie
Yehonatan
Bawiliśmy się tym (PHP 7.x): PNG: imagealphablending ($ targetImage, false); // jeśli prawda na PNG: czarne tło GIF: imagealphablending ($ targetImage, true); // jeśli fałsz na GIF-ach: czarne tło
Jonny
9

Dodatek, który może pomóc niektórym osobom:

Podczas budowania obrazu możliwe jest przełączanie blokowania obrazu. W konkretnym przypadku, gdy tego potrzebowałem, chciałem połączyć kilka półprzezroczystych plików PNG na przezroczystym tle.

Najpierw ustaw imagealphablending na false i wypełnij nowo utworzony obraz w prawdziwych kolorach przezroczystym kolorem. Gdyby blokowanie obrazu było prawdziwe, nic by się nie wydarzyło, ponieważ przezroczyste wypełnienie połączyłoby się z czarnym domyślnym tłem i dałoby czarny kolor.

Następnie przełączasz imagealphablending na true i dodajesz obrazy PNG do płótna, pozostawiając część tła widoczną (tj. Nie wypełniając całego obrazu).

W rezultacie otrzymujemy obraz z przezroczystym tłem i kilkoma połączonymi obrazami PNG.

Jorrit Schippers
źródło
6

Stworzyłem funkcję zmiany rozmiaru obrazu, takiego jak JPEG / GIF / PNG, copyimageresamplea obrazy PNG nadal zachowują przezroczystość:

$myfile=$_FILES["youimage"];

function ismyimage($myfile) {
    if((($myfile["type"] == "image/gif") || ($myfile["type"] == "image/jpg") || ($myfile["type"] == "image/jpeg") || ($myfile["type"] == "image/png")) && ($myfile["size"] <= 2097152 /*2mb*/) ) return true; 
    else return false;
}

function upload_file($myfile) {         
    if(ismyimage($myfile)) {
        $information=getimagesize($myfile["tmp_name"]);
        $mywidth=$information[0];
        $myheight=$information[1];

        $newwidth=$mywidth;
        $newheight=$myheight;
        while(($newwidth > 600) || ($newheight > 400 )) {
            $newwidth = $newwidth-ceil($newwidth/100);
            $newheight = $newheight-ceil($newheight/100);
        } 

        $files=$myfile["name"];

        if($myfile["type"] == "image/gif") {
            $tmp=imagecreatetruecolor($newwidth,$newheight);
            $src=imagecreatefromgif($myfile["tmp_name"]);
            imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $mywidth, $myheight);
            $con=imagegif($tmp, $files);
            imagedestroy($tmp);
            imagedestroy($src);
            if($con){
                return true;
            } else {
                return false;
            }
        } else if(($myfile["type"] == "image/jpg") || ($myfile["type"] == "image/jpeg") ) {
            $tmp=imagecreatetruecolor($newwidth,$newheight);
            $src=imagecreatefromjpeg($myfile["tmp_name"]); 
            imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $mywidth, $myheight);
            $con=imagejpeg($tmp, $files);
            imagedestroy($tmp);
            imagedestroy($src);
            if($con) {  
                return true;
            } else {
                return false;
            }
        } else if($myfile["type"] == "image/png") {
            $tmp=imagecreatetruecolor($newwidth,$newheight);
            $src=imagecreatefrompng($myfile["tmp_name"]);
            imagealphablending($tmp, false);
            imagesavealpha($tmp,true);
            $transparent = imagecolorallocatealpha($tmp, 255, 255, 255, 127);
            imagefilledrectangle($tmp, 0, 0, $newwidth, $newheight, $transparent); 
            imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $mywidth, $myheight);
            $con=imagepng($tmp, $files);
            imagedestroy($tmp);
            imagedestroy($src);
            if($con) {
                return true;
            } else {
                return false;
            }
        }   
    } else
          return false;
}
pricopz
źródło
3
Przeczytanie całego kodu jest dość uciążliwe, aby dowiedzieć się, dlaczego w tym kodzie zachowana jest przejrzystość w stosunku do kodu w pytaniu.
eh9
$transparent = imagecolorallocatealpha($tmp, 255, 255, 255, 127); imagefilledrectangle($tmp, 0, 0, $newwidth, $newheight, $transparent);
Pominąłem
Ta odpowiedź wygląda podobnie do stackoverflow.com/a/279310/470749 .
Ryan
5

Przypuszczam, że to może załatwić sprawę:

$uploadTempFile = $myField[ 'tmp_name' ]
list( $uploadWidth, $uploadHeight, $uploadType ) 
  = getimagesize( $uploadTempFile );

$srcImage = imagecreatefrompng( $uploadTempFile );

$targetImage = imagecreatetruecolor( 128, 128 );

$transparent = imagecolorallocate($targetImage,0,255,0);
imagecolortransparent($targetImage,$transparent);
imagefilledrectangle($targetImage,0,0,127,127,$transparent);

imagecopyresampled( $targetImage, $srcImage, 
                    0, 0, 
                    0, 0, 
                    128, 128, 
                    $uploadWidth, $uploadHeight );

imagepng(  $targetImage, 'out.png', 9 );

Wadą jest to, że obraz zostanie pozbawiony każdego 100% zielonych pikseli. Tak czy inaczej, mam nadzieję, że to pomoże :)

Linus Unnebäck
źródło
Jeśli ustawisz wyjątkowo brzydki kolor, którego prawie żaden obraz nie będzie używał, może być bardzo pomocny.
Cory
1
Przyjęta odpowiedź nie zadziałała dla mnie. Użycie tej odpowiedzi w przypadku imagecreate(...)zadziałało. Tworzysz obraz, który zostaje wypełniony pierwszym przydzielonym kolorem. Następnie ustawiasz ten kolor na przezroczysty. Jeśli ustawienie alphablending ma wartość true dla obrazu docelowego, oba obrazy zostaną scalone, a przezroczystość będzie działać poprawnie.
Sumurai8,
2

Odnosząc się do zachowania przezroczystości, to tak, jak podano w innych postach, imagesavealpha () musi być ustawione na true, aby użyć flagi alfa, imagealphablending () musi być ustawione na false, w przeciwnym razie nie będzie działać.

W twoim kodzie zauważyłem również dwie drobne rzeczy:

  1. Nie musisz dzwonić, getimagesize()aby uzyskać szerokość / wysokośćimagecopyresmapled()
  2. Wartość $uploadWidthi $uploadHeightpowinna być -1wartością, ponieważ serie koordynacyjne zaczynają się od 0a nie 1, więc skopiowałaby je do pustego piksela. Zastąpienie go: imagesx($targetImage) - 1i imagesy($targetImage) - 1, względnie powinno wystarczyć :)
Kalle
źródło
0

Oto mój całkowity kod testowy. Mi to pasuje

$imageFileType = pathinfo($_FILES["image"]["name"], PATHINFO_EXTENSION);
$filename = 'test.' . $imageFileType;
move_uploaded_file($_FILES["image"]["tmp_name"], $filename);

$source_image = imagecreatefromjpeg($filename);

$source_imagex = imagesx($source_image);
$source_imagey = imagesy($source_image);

$dest_imagex = 400;
$dest_imagey = 600;
$dest_image = imagecreatetruecolor($dest_imagex, $dest_imagey);

imagecopyresampled($dest_image, $source_image, 0, 0, 0, 0, $dest_imagex, $dest_imagey, $source_imagex, $source_imagey);

imagesavealpha($dest_image, true);
$trans_colour = imagecolorallocatealpha($dest_image, 0, 0, 0, 127);
imagefill($dest_image, 0, 0, $trans_colour);

imagepng($dest_image,"test1.png",1);
Md. Imadul Islam
źródło
0

Zwróć uwagę na obraz źródłowy widthi heightwartości, które są przekazywane do imagecopyresampledfunkcji. Jeśli są większe niż rzeczywisty rozmiar obrazu źródłowego, reszta obszaru obrazu zostanie wypełniona czarnym kolorem.

Stefan
źródło
0

Połączyłem odpowiedzi od ceejayoz i Cheekysoft, co dało dla mnie najlepszy wynik. Bez imagealphablending () i imagesavealpha () obraz nie jest wyraźny:

$img3 = imagecreatetruecolor(128, 128);
imagecolortransparent($img3, imagecolorallocate($img3, 0, 0, 0));
imagealphablending( $img3, false );
imagesavealpha( $img3, true );
imagecopyresampled($img3, $srcImage, 0, 0, 0, 0, 128, 128, $uploadWidth, $uploadHeight);
imagepng($img3, 'filename.png', 9);
Marco
źródło