Частичный черный фон, когда водяной знак PNG-изображения с GD PHP

Я собрал класс PHP для выполнения различных связанных с изображениями функций с использованием функций GD для PHP.

Он отлично работает для всех типов изображений. Повернуть, перевернуть, изменить размер, урожай и, в меньшей степени, водяной знак.

Все, кроме последнего, отлично работают. Например, после нескольких изменений, повернутые изображения PNG сохранили свою прозрачность, а до того, как они потеряли это, и фон стал черным. Обычная проблема, похоже. Но все работает сейчас.

Там, где я все еще застреваю, водяное знакомство с изображением PNG с другим изображением PNG. Кажется, он отлично работает с JPG и другими изображениями. Это код (упрощенный):

public function writeWatermarkSimple() { $watermarkFile = 'watermark.png'; $watermarkImage = imagecreatefrompng($watermarkFile); imagealphablending($watermarkImage, false); imagesavealpha($watermarkImage, true); $imageFile = 'image.png'; $baseImage = imagecreatefrompng($imageFile); imagealphablending($baseImage, false); imagesavealpha($baseImage, true); $marginH = imagesx($baseImage) - imagesx($watermarkImage); $marginV = imagesy($baseImage) - imagesy($watermarkImage); $cut = imagecreatetruecolor(imagesx($watermarkImage), imagesy($watermarkImage)); imagecopy($cut, $baseImage, 0, 0, $marginH, $marginV, imagesx($watermarkImage), imagesy($watermarkImage)); imagecopy($cut, $watermarkImage, 0, 0, 0, 0, imagesx($watermarkImage), imagesy($watermarkImage)); imagecopymerge($baseImage, $cut, $marginH, $marginV, 0, 0, imagesx($watermarkImage), imagesy($watermarkImage), 80); if (!imagepng($baseImage, 'watermarked_image.png')) { return false; } return true; } 

Это было собрано вместе с различными руководствами и советами, которые люди дали по аналогичной проблеме. Опять же, отлично работает с изображениями JPG и водяными знаками PNG, но не PNG и PNG.

Некоторые примеры изображений:

http://img.ruphp.com/php/hHRWinj.png – Это водяной знак, который я использую. http://img.ruphp.com/php/6sy8Ncs.png – Это образ, к которому я применяю водяной знак. http://img.ruphp.com/php/ghovYLm.png – Это конечный результат.

Бит, который мне интересен, заключается в том, что любая часть водяного знака, наложенная на непрозрачную часть изображения, работает нормально. Просто все остальное имеет черный фон.

Это заставляет меня поверить, что я близок, и я надеюсь, что опыт ваших прекрасных людей может привести меня к решению.

Спасибо, что за чтение.

Итак, я не отказываюсь от поиска правильного ответа, чтобы сделать это, используя GD. Тем не менее, я был очень рад найти, что то, что нужно до 30 строк кода с GD, может быть достигнуто с использованием ImageMagick намного меньше:

  $image = new Imagick(); $image->readimage($this->_image); $watermark = new Imagick(); $watermark->readimage($this->_watermark->_getImage()); $watermark->evaluateImage(Imagick::EVALUATE_DIVIDE, 2, Imagick::CHANNEL_ALPHA); $image->compositeImage($watermark, imagick::COMPOSITE_OVER, $marginH, $marginV); 

Так это до (с GD): http://img.ruphp.com/php/AlS0TcO.png

И после (с ImageMagick и кодом выше): http://img.ruphp.com/php/zBxlC3R.png

Если у кого-то есть ответ, который является чисто GD, я был бы очень благодарен.

В последнее время возникли некоторые подобные проблемы, и хотя это может не точно решить вашу проблему, это были некоторые полезные открытия, которые я сделал.

В моем случае у меня есть оригинальное изображение .jpg и водяной знак .png . Изображение водяного знака имеет полностью прозрачный фон. Я хотел указать непрозрачность в моем скрипте и изменить его непрозрачность водяного знака, прежде чем размещать его поверх изображения Origina. Большинство сообщений, посвященных водяному знаку PHP, предполагают, что исходный файл водяного знака .png уже имеет сплошную водяную знак, установленную на правильную непрозрачность, а не ее изменение через скрипт.

  1. gd не понравился 24-разрядный .png и вызвал некоторые проблемы. Переключение на 8 бит разрешено с помощью gd . С другой стороны, imagick работает очень хорошо с 24-бит .png и конечный результат кажется лучше.
  2. Для меня использование gd работало отлично, если я открывал исходный водяной знак .png и использовал imagecopymerge() чтобы установить прозрачность водяных знаков. Если, однако, я попытался сначала масштабировать исходный водяной знак .png (который имеет прозрачный фон), тогда я получаю аналогичные результаты, как у вас с черным или белым фоном, где находится изображение водяного знака. См. Как изменить размер PNG с прозрачностью в PHP? для частичного решения, сначала заполнив новое изображение wm прозрачным прямоугольником. Для меня это все еще создавало непрозрачный белый фон для конечного результата, независимо от того, что я пробовал.
  3. Я переключился на imagick и использовал setImageOpacity() чтобы изменить прозрачность моего водяного знака .png прежде чем применять его поверх моего исходного изображения, и я все еще получал тот же эффект с черным фоном. Наконец, прочитайте в PHP doc для setImageOpacity() что если исходный .png имеет какие-либо прозрачные пиксели, и вы пытаетесь уменьшить непрозрачность, эти пиксели становятся непрозрачными (черные) с примененной новой прозрачностью. Вместо этого нужно использовать функцию evaluateImage() . Это вместо этого будет оценивать только один альфа-канал каждого пикселя и делить на указанное число.
  4. Я предполагаю, что проблема с черно-белым фоном с gd , вероятно, связана с аналогичными способами, которые он рассматривает альфа-каналы при масштабировании / объединении по сравнению с imagick и если вы хотите сделать все это в gd вам просто нужно найти аналогичный способ оценки и манипулировать alpha каналом на пиксель, потому что «простые» способы, кажется, принимают уже прозрачный фон и делают его непрозрачным.

Итак, решение:

Предполагая, что вы хотите применить свой водяной знак с непрозрачностью 45%, и вы используете imagick , вместо этого:

$watermark->setImageOpacity(.45);

сделай это

$watermark->evaluateImage(Imagick::EVALUATE_DIVIDE, (1/.45), Imagick::CHANNEL_ALPHA);

Вам нужно разделить 1 на непрозрачность, чтобы получить демонизатор, с помощью которого функция будет делить значение альфа-канала для каждого пикселя. В этом случае 1/.45 = 2.2222 , поэтому функция будет делить альфа-канал каждого пикселя на 2.2222 . Это означает, что сплошной пиксель (альфа 1 ) приведет к получению 1/2.2222 или .45 альфа или прозрачности при завершении. Любые пиксели, которые были уже прозрачными (alpha 0 ), оставались бы прозрачными, потому что 0 разделенное на что-нибудь, всегда есть что? Нуль!

После того, как вы измените прозрачность водяного знака, вы можете использовать compositeImage() для слияния водяного знака с исходным изображением.