5 вариантов добавления watermark (водяной знак) на изображение, средствами PHP

Author Автор: Роман Чернышов    Опубликовано: 11 мая 2012

Данный пост содержит пять примеров, вариантов, добавления водяного знака на изображение с помощью PHP. Думаю о пользе добавления watermark’а все в курсе, в первую очередь это своеобразная защита изображения от несанкционированного копирования его и последующее использование на других сайтах. Во вторых, добавление ненавязчивого водяного знака это хороший способ лишний раз прорекламировать свой сайт(бренд, фирму) пользователю, который не всегда читает текст, но всегда смотрит картинки.

Все примеры, скрипты использовались мною в разных проектах, часть их была найдена в сети, что-то было существенно переделано, что-то оставлено без изменений.

И так приступим.

Пример 1.  Данный весьма простой скрипт можно отнести к официальным примерам, он размещен на php.net. Все предельно просто. на одно изображение накладывается другое (PNG с прозрачностью).

// Загрузка штампа и фото, для которого применяется водяной знак (называется штамп или печать)
$stamp = imagecreatefrompng('stamp.png');
$im = imagecreatefromjpeg('photo.jpeg');
 
// Установка полей для штампа и получение высоты/ширины штампа
$marge_right = 10;
$marge_bottom = 10;
$sx = imagesx($stamp);
$sy = imagesy($stamp);
// Копирование изображения штампа на фотографию с помощью смещения края
// и ширины фотографии для расчета позиционирования штампа. 
imagecopy($im, $stamp, imagesx($im) - $sx - $marge_right, 
imagesy($im) - $sy - $marge_bottom, 0, 0, imagesx($stamp), 
imagesy($stamp));
 
// Вывод и освобождение памяти
header('Content-type: image/png');
imagepng($im);
imagedestroy($im);

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

function add_watermark($img, $text, $font, $r = 128, $g = 128, $b = 128, $alpha = 100)
  {
   //получаем ширину и высоту исходного изображения
   $width = imagesx($img);
   $height = imagesy($img);
   //угол поворота текста
   $angle =  -rad2deg(atan2((-$height),($width))); 
 
   //добавляем пробелы к строке
   $text = " ".$text." ";
 
   $c = imagecolorallocatealpha($img, $r, $g, $b, $alpha);
   $size = (($width+$height)/2)*2/strlen($text);
   $box  = imagettfbbox ( $size, $angle, $font, $text );
   $x = $width/2 - abs($box[4] - $box[0])/2;
   $y = $height/2 + abs($box[5] - $box[1])/2;
 
   //записываем строку на изображение
   imagettftext($img,$size ,$angle, $x, $y, $c, $font, $text);
   return $img;
  }

Параметры функции следующие

$img – идентификатор изображения, на которое добавляется водяной знак
$text – текст надписи
$font – имя файла шрифта .ttf
$r,$g,$b – цвет надписи в формате RGB
$alpha – уровень прозрачности , от 0 до 128

Пример вызова функции:

$img = imagecreatefromjpeg('elbrus.jpg');
$image = add_watermark($img2,'Grigorieff.ru','erasbd.ttf');
 
//выводим изображение
imageJPEG($image);
 
//освобождаем память
imagedestroy($image);
imagedestroy($img2);

Пример 3. Не плохая функция, но работает с глобальной переменной хранящей адрес изображения которое будет накладываться на исходное (адрес watermark’а). Вариант на любителя.

/**
 * Наложение водяного знака в виде изображения
 * @param $oldimage_name - исходное изображение
 * @param $new_image_name - выходное изображение
 * @return Boolean
 */
function watermark_image($oldimage_name, $new_image_name){
	// получаем имя изображения, используемого в качестве водяного знака 
	global $image_path;
	// получаем размеры исходного изображения
	list($owidth,$oheight) = getimagesize($oldimage_name);
	// задаем размеры для выходного изображения 
	$width = 600;
	$height = 300; 
	// создаем выходное изображение размерами, указанными выше
	$im = imagecreatetruecolor($width, $height);
	$img_src = imagecreatefromjpeg($oldimage_name);
	// наложение на выходное изображение, исходного
	imagecopyresampled($im, $img_src, 0, 0, 0, 0, $width, $height, $owidth, $oheight);
	$watermark = imagecreatefrompng($image_path);
	// получаем размеры водяного знака
	list($w_width, $w_height) = getimagesize($image_path);
	// определяем позицию расположения водяного знака 
	$pos_x = $width - $w_width; 
	$pos_y = $height - $w_height;
	// накладываем водяной знак
	imagecopy($im, $watermark, $pos_x, $pos_y, 0, 0, $w_width, $w_height);
	// сохраняем выходное изображение, уже с водяным знаком в формате jpg и качеством 100
	imagejpeg($im, $new_image_name, 100);
	// уничтожаем изображения
	imagedestroy($im);
	unlink($oldimage_name);
	return true;
}

Пример 4. Пример добавление водяного знака из 8 битного PNG файла. Оформлен в класс (автор посчитал это забавным).

/*
*   @param $main_img_obj – идентификатор изображения, на которое добавляется надпись
*   @param $watermark_img_obj – ид. изображения прозрачного png8
*   @param $alpha_level – прозрачность (0 – прозрачное, 100 – полностью непрозрачное)
*   @return $main_img_obj - указатель изображения
*/
class watermark2
{
function create_watermark( $main_img_obj, $watermark_img_obj, $alpha_level = 100 )
{
$watermark_width = imagesx($watermark_img_obj);
$watermark_height = imagesy($watermark_img_obj);
 
$dest_x = imagesx($main_img_obj) - $watermark_width - 5;
$dest_y = imagesy($main_img_obj) - $watermark_height - 5;
imagecopymerge($main_img_obj, $watermark_img_obj, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height, $alpha_level);
 
return $main_img_obj;
}
}

Пример вызова функции:

$watermark = new watermark2();
$img = imagecreatefromjpeg(“image.jpg”);
$water = imagecreatefrompng(“watermark8.png”);
$im=$watermark->create_watermark($img,$water,10);
imagejpeg($im,”result.jpg”);

Пример 5. Самый интересный пример, оформлен в PHP класс. Работает в PNG 24. есть возможность настройки параметров прозрачности. В интернетах этот вариант выкладывали и обсуждали не раз.

/*
*   @param $main_img_obj – идентификатор изображения, на которое добавляется надпись
*   @param $watermark_img_obj – ид. изображения прозрачного png8
*   @param $alpha_level – прозрачность (0 – прозрачное, 100 – полностью непрозрачное)
*   @return $main_img_obj - указатель изображения
*/
class watermark3{
 
	# given two images, return a blended watermarked image
	function create_watermark( $main_img_obj, $watermark_img_obj, $alpha_level = 100 ) {
		$alpha_level	/= 100;	# convert 0-100 (%) alpha to decimal

		# calculate our images dimensions
		$main_img_obj_w	= imagesx( $main_img_obj );
		$main_img_obj_h	= imagesy( $main_img_obj );
		$watermark_img_obj_w	= imagesx( $watermark_img_obj );
		$watermark_img_obj_h	= imagesy( $watermark_img_obj );
 
		# determine center position coordinates
		$main_img_obj_min_x	= floor( ( $main_img_obj_w / 2 ) - ( $watermark_img_obj_w / 2 ) );
		$main_img_obj_max_x	= ceil( ( $main_img_obj_w / 2 ) + ( $watermark_img_obj_w / 2 ) );
		$main_img_obj_min_y	= floor( ( $main_img_obj_h / 2 ) - ( $watermark_img_obj_h / 2 ) );
		$main_img_obj_max_y	= ceil( ( $main_img_obj_h / 2 ) + ( $watermark_img_obj_h / 2 ) ); 
 
		# create new image to hold merged changes
		$return_img	= imagecreatetruecolor( $main_img_obj_w, $main_img_obj_h );
 
		# walk through main image
		for( $y = 0; $y < $main_img_obj_h; $y++ ) {
			for( $x = 0; $x < $main_img_obj_w; $x++ ) {
				$return_color	= NULL;
 
				# determine the correct pixel location within our watermark
				$watermark_x	= $x - $main_img_obj_min_x;
				$watermark_y	= $y - $main_img_obj_min_y;
 
				# fetch color information for both of our images
				$main_rgb = imagecolorsforindex( $main_img_obj, imagecolorat( $main_img_obj, $x, $y ) );
 
				# if our watermark has a non-transparent value at this pixel intersection
				# and we're still within the bounds of the watermark image
				if (	$watermark_x >= 0 && $watermark_x < $watermark_img_obj_w &&
							$watermark_y >= 0 && $watermark_y < $watermark_img_obj_h ) {
					$watermark_rbg = imagecolorsforindex( $watermark_img_obj, imagecolorat( $watermark_img_obj, $watermark_x, $watermark_y ) );
 
					# using image alpha, and user specified alpha, calculate average
					$watermark_alpha	= round( ( ( 127 - $watermark_rbg['alpha'] ) / 127 ), 2 );
					$watermark_alpha	= $watermark_alpha * $alpha_level;
 
					# calculate the color 'average' between the two - taking into account the specified alpha level
					$avg_red		= $this->_get_ave_color( $main_rgb['red'],		$watermark_rbg['red'],		$watermark_alpha );
					$avg_green	= $this->_get_ave_color( $main_rgb['green'],	$watermark_rbg['green'],	$watermark_alpha );
					$avg_blue		= $this->_get_ave_color( $main_rgb['blue'],	$watermark_rbg['blue'],		$watermark_alpha );
 
					# calculate a color index value using the average RGB values we've determined
					$return_color	= $this->_get_image_color( $return_img, $avg_red, $avg_green, $avg_blue );
 
				# if we're not dealing with an average color here, then let's just copy over the main color
				} else {
					$return_color	= imagecolorat( $main_img_obj, $x, $y );
 
				} # END if watermark

				# draw the appropriate color onto the return image
				imagesetpixel( $return_img, $x, $y, $return_color );
 
			} # END for each X pixel
		} # END for each Y pixel

		# return the resulting, watermarked image for display
		return $return_img;
 
	} # END create_watermark()

	# average two colors given an alpha
	function _get_ave_color( $color_a, $color_b, $alpha_level ) {
		return round( ( ( $color_a * ( 1 - $alpha_level ) ) + ( $color_b	* $alpha_level ) ) );
	} # END _get_ave_color()

	# return closest pallette-color match for RGB values
	function _get_image_color($im, $r, $g, $b) {
		$c=imagecolorexact($im, $r, $g, $b);
		if ($c!=-1) return $c;
		$c=imagecolorallocate($im, $r, $g, $b);
		if ($c!=-1) return $c;
		return imagecolorclosest($im, $r, $g, $b);
	} # EBD _get_image_color()

}

Пример вызова функции:

$watermark = new watermark3();
$img = imagecreatefromjpeg(“image.jpg”);
$water = imagecreatefrompng(“watermark24.png”);
$im=$watermark->create_watermark($img,$water,10);
imagejpeg($im,”result.jpg”);

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

Category PHP     Tags ,

5 комментариев to “5 вариантов добавления watermark (водяной знак) на изображение, средствами PHP”

  • Вячеслав 15.05.2021 в 6:42 пп

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

    • Роман Чернышов 18.05.2021 в 1:12 пп

      Заменить строки:
      # determine center position coordinates
      $main_img_obj_min_x = floor( ( $main_img_obj_w / 2 ) — ( $watermark_img_obj_w / 2 ) );
      $main_img_obj_max_x = ceil( ( $main_img_obj_w / 2 ) + ( $watermark_img_obj_w / 2 ) );
      $main_img_obj_min_y = floor( ( $main_img_obj_h / 2 ) — ( $watermark_img_obj_h / 2 ) );
      $main_img_obj_max_y = ceil( ( $main_img_obj_h / 2 ) + ( $watermark_img_obj_h / 2 ) );

      На:
      # determine center position coordinates
      $main_img_obj_min_x = $main_img_obj_w — 20 — $watermark_img_obj_w;
      $main_img_obj_max_x = $main_img_obj_w — 20;
      $main_img_obj_min_y = $main_img_obj_h — 20 — $watermark_img_obj_h;
      $main_img_obj_max_y = $main_img_obj_h — 20;

  • Евгений 09.12.2021 в 4:06 пп

    Подскажите пожалуйста, как в такой конструкции
    if ($width_orig != $width || $height_orig != $height) {
    $image = new Image(DIR_IMAGE . $image_old);
    $image->resize($width, $height);
    $image->watermark(new Image(DIR_IMAGE . ‘watermark.png’), ‘bottomright’);
    $image->save(DIR_IMAGE . $image_new);
    } else {
    copy(DIR_IMAGE . $image_old, DIR_IMAGE . $image_new);
    }
    указать размер watermark не более 50% от ширины картинки на которую он накладывается?
    И можно ли сделать отступ снизу 20%?

    • Роман Чернышов 11.12.2021 в 2:07 дп

      Добрый день, для такого наложения необходима еще одна функция преобразования, а именно изменения размера накладываемого изображения — ресайз. Параметры ресайза можно высчитать на основании размера основного изображения. Вот мой пост про данную функцию(ресайз) с примером кода: https://rche.ru/1042_php-izmenenie-razmera-izobrazheniya-thumbnail.html

      • Евгений 21.12.2021 в 9:27 пп

        Благодарю за ответ! Пошел другим путем, нашел модуль готовый.

Оставить комментарий

Автор блога
Роман Чернышов
Веб-разработчик,
Full Stack
Senior, Architect
PHP, JavaScript, Node.JS, Python, HTML 5, CSS 3, MySQL, Bash, Linux Admin
Заказать работу
предложить оффер

Моя книга
Книга. Веб-разработчик. Легкий вход в профессию
Печатная книга
Веб-разработчик.
Легкий вход в профессию
Оформить предзаказ
Последние вопросы
Список вопросов
Последние комментарии
Меню

Archive

Мои проекты
Insurance CMS Love Crm CMS Совместные покупки Мой PHP Framework Хостинг для моих клиентов Лицензии на мой софт и поддержка