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 );

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 );

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]);

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.


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.


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;
}

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 :)


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ć :)

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);

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.


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);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.