I’ve always been very lazy to watermark my photos. Adobe’s Lightroom 2 doesn’t currently support automatic image watermarking and the only viable option is to launch Photoshop CS4 and apply the watermark to each and every photo. Very tedious, isn’t it?

So I thought to myself, why not just watermark images on-the-fly on my site? And with that, I explored the webs to find an efficient way to do it, but most are bugged with two problems – you can only use a PNG-8 image watermark, and your images will suffer from terribly devastating JPEG compression artefacts. Oh, the horror!

And here I am, with my shiny and polished code. Read on to find out more!

For you impatient code monkeys, here is the code itself:

⟨?php
$imagesource = $_GET['src'];
$filetype = substr($imagesource,strlen($imagesource)-4,4);
$filetype = strtolower($filetype);
if($filetype == ".gif") header('content-type: image/gif');
if($filetype == ".jpg") header('content-type: image/jpeg');
if($filetype == ".png") header('content-type: image/png');
$watermark = imagecreatefrompng('watermark.png');
$watermark_width = imagesx($watermark);
$watermark_height = imagesy($watermark);
$image = imagecreatetruecolor($watermark_width, $watermark_height);
if($filetype == ".gif") $image = imagecreatefromgif($imagesource);
if($filetype == ".jpg") $image = imagecreatefromjpeg($imagesource);
if($filetype == ".png") $image = imagecreatefrompng($imagesource);
$size = getimagesize($imagesource);
$dest_x = 10;
$dest_y = $size[1] - $watermark_height - 5;
imagecopy($image, $watermark, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height);
if($filetype == ".gif") imagegif($image);
if($filetype == ".jpg") imagejpeg($image, NULL, 100);
if($filetype == ".png") imagepng($image);
imagedestroy($image);
imagedestroy($watermark);
?⟩

I shall explain the code in detail.

$imagesource = $_GET['src'];

This basically grabs whatever string is present in the URL after the src. For example, in the URL http://www.example.com/watermark.php?src=image.jpg, the image.jpg will be extracted as a $imagesource variable.

$filetype = substr($imagesource,strlen($imagesource)-4,4);
$filetype = strtolower($filetype);

This identifies the image filetype based on the extension.

if($filetype == ".gif") header('content-type: image/gif');
if($filetype == ".jpg") header('content-type: image/jpeg');
if($filetype == ".png") header('content-type: image/png');

If the identified filetypes match either GIF, JPG or PNG, this appends an image content-type to the header which the browser can parse correctly.

$watermark = imagecreatefrompng('watermark.png');

This loads the image watermark watermark.png into the server memory. It should be in the same directory. If you are using an image watermark that is not a PNG, you should replace the imagecreatefrompng with imagecreatefromjpeg or imagecreatefromgif appropriately.

$watermark_width = imagesx($watermark);
$watermark_height = imagesy($watermark);
$image = imagecreatetruecolor($watermark_width, $watermark_height);

This checks the width and height of the image watermark, and then creates a new image in the server memory with the same dimensions.

if($filetype == ".gif") $image = imagecreatefromgif($imagesource);
if($filetype == ".jpg") $image = imagecreatefromjpeg($imagesource);
if($filetype == ".png") $image = imagecreatefrompng($imagesource);

Again, the file checks the image source and imports the image into the memory as a $image variable.

$size = getimagesize($imagesource);
$dest_x = 5;
$dest_y = $size[1] - $watermark_height - 5;

This determines the destination position of the image watermark on the image. In the above example, it is located at the bottom left corner with a 5px padding from the edges. Here are some examples on different positions, each with a 5px padding from the edges:

  • Top left:
  • $dest_x = 5;
    $dest_y = 5;

  • Top right:
  • $dest_x = $size[0] - $watermark_height - 5;
    $dest_y = 5;

  • Bottom right:
  • $dest_x = $size[0] - $watermark_height - 5;
    $dest_y = $size[1] - $watermark_height - 5;

    You can experiment around with the numbers to get the exact position that you want.

    imagecopy($image, $watermark, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height);

    This merges both the image watermark and the image. Most of the tutorials on the internet use imagecopymerged instead, however I found that it is incompatible with PNG-24 and PNG-32. imagecopy allows transparency alpha image watermarks to be used without fuss.

    if($filetype == ".gif") imagegif($image);
    if($filetype == ".jpg") imagejpeg($image, NULL, 100);
    if($filetype == ".png") imagepng($image);

    Now, the code checks the file type again and outputs the image in memory as the appropriate image type. Note that the imagejpeg($image, NULL, 100); has extra parameters. By default, imagejpeg exports images with a 75% quality which results in unsatisfactory compression artefacts. By pushing it up to 100%, minimal artefacts are visible although this increases file size and consumes more server resources.

    imagedestroy($image);
    imagedestroy($watermark);

    Finally, the image and image watermark are removed from the server memory.

    You may modify, publish and redistribute this code freely. However, I would appreciate that you attribute or link back.