Наложение ватермарков (ватермарок, watermarks) на фотографии с помощью PHP

Нередко возникает необходимость защитить фотографии на сайте от воровства. Особенно популярно в различных каталогах и т.д. Самый простой и надежный способ — наложение так называемых ватермарков.

Создается картинка, размерами MxN с прозрачным фоном и полупрозрачной надписью с именем домена. Сохраняется в формате PNG-24. Вся штука кроется в библиотеке GD при наложении друг на друга картинок. Первая картинка, собственно, сама фотография; на нее накладывается другая — полупрозрачная ватермарка.

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

//Загружаем фотографию из нужного формата. Здесь JPG.
$image = imagecreatefromjpeg($filename);
//Можно создать небольшую функцию определения формата и загрузки изображения.
//Или воспользоваться imagecreatefromstring
 
//Загружаем нашу ватермарку
$water=imagecreatefrompng($file);
//Определяем ее ширину
$water_width=imagesx($water);
//Определяем ее высоту
$water_height=imagesy($water);
 
$img_width=imagesx($image);
$img_height=imagesy($image);
 
//Вычисляем координаты, чтобы ватермарка оказалась посередине фотографии        
$dest_x = ($img_width-$water_width)/2;
$dest_y = ($img_height-$water_height)/2;
 
imagealphablending($this->image, 1);
imagealphablending($this->water, 1);
imagesavealpha($this->image, 1);
imagesavealpha($this->water, 1);
imagecopyresampled($image, $water, $dest_x, $dest_y,0, 0, $water_width, $water_height,$water_width, $water_height);
 
//Сохраняем. Вторым параметром идет имя файла, третьим - качество. Чем выше, тем лучше.
imagejpeg($image, $filename, 95);

Данный код прекрасно работает, если фотография или картинка загружается из формата JPG. Если у нас формат PNG или GIF то после наложения получим несколько не тот результат, который бы хотелось. То фон становится непрозрачным а то и вовсе получаем белое (серое, зеленое) пятно на месте предполагаемой ватермарки. Причем совершенно непрозрачное и размером во весь холст. Так что значительная часть фото становится замазана а-ля цензура.

Как оказалось, этот баг давно известен разработчикам библиотеки GD, но очевидно они не спешат его исправлять. С проблемой впервые столкнулись примерно в 2010 году. Сейчас на дворе 2015, я использую версию библиотеки 2.0.35 и все ок, баг на месте. ? Хорошо поддерживается формат PNG-8, но беда в том, что у него нет прозрачности.

Теперь о том, как с этой ситуацией бороться. К сожалению чуточку костыльный способ, требует чуть больше ресурсов, зато без проблем работает на всех типах исходных картинок:

//Первая часть точно такая же. Никакой разницы.
//$dest_y = ($img_height-$water_height)/2;
 
//А вот здесь начинаются изменения...
//Подготавливаем пустой холст размером с нашу ватермарку
$cut = imagecreatetruecolor($water_width, $water_height);
//Копируем туда кусок исходной картинки из середины.
//Ровно то место, куда будет наложена ватермарка
imagecopy($cut, $image, 0, 0, $dest_x, $dest_y, $water_width, $water_height);
//Теперь копируем туда же саму ватермарку.
imagecopy($cut, $water, 0, 0, 0, 0, $water_width, $water_height);
//Соединяем наш подготовленный холст с целевой картинкой
imagecopymerge($image, $cut, $dest_x, $dest_y, 0, 0, $water_width, $water_height, 100);
 
//Сохраняем
imagejpeg($image, $filename, 95);

Вот собственно и все. Теперь можем ставить watermarks на изображения любых (поддерживаемых библиотекой GD) форматов.
За подробными разъяснениями по параметрам функций, обращайтесь к официальной документации.