下面给你一份可直接运行、基于 PHP GD 库的「图片水印」完整示例,包含
✔ 图片水印
✔ 透明度控制
✔ 多格式支持
✔ 常见坑说明

非常适合博客、CMS、上传系统使用。


一、效果说明

在原图右下角添加一张 PNG 水印图(支持透明)


二、环境要求

  • PHP ≥ 7.0
  • 开启 GD 扩展
php -m | grep gd


三、完整示例代码(图片水印)

<?php

/**
 * 给图片添加图片水印
 * @param string $src 原图路径
 * @param string $water 水印图路径(PNG推荐)
 * @param string $savePath 保存路径(为空则覆盖原图)
 * @param int $alpha 水印透明度 0-100
 */
function addImageWatermark($src, $water, $savePath = '', $alpha = 60)
{
    if (!file_exists($src) || !file_exists($water)) {
        return false;
    }

    // 获取原图信息
    $srcInfo = getimagesize($src);
    $waterInfo = getimagesize($water);

    list($srcW, $srcH, $srcType) = $srcInfo;
    list($waterW, $waterH) = $waterInfo;

    // 创建原图资源
    switch ($srcType) {
        case IMAGETYPE_JPEG:
            $srcImg = imagecreatefromjpeg($src);
            break;
        case IMAGETYPE_PNG:
            $srcImg = imagecreatefrompng($src);
            break;
        case IMAGETYPE_GIF:
            $srcImg = imagecreatefromgif($src);
            break;
        default:
            return false;
    }

    // 创建水印资源
    $waterImg = imagecreatefrompng($water);

    // 水印位置(右下角,留 10px 边距)
    $dstX = $srcW - $waterW - 10;
    $dstY = $srcH - $waterH - 10;

    // 合并水印(支持透明)
    imagecopymerge(
        $srcImg,
        $waterImg,
        $dstX,
        $dstY,
        0,
        0,
        $waterW,
        $waterH,
        $alpha
    );

    // 保存图片
    $savePath = $savePath ?: $src;

    switch ($srcType) {
        case IMAGETYPE_JPEG:
            imagejpeg($srcImg, $savePath, 90);
            break;
        case IMAGETYPE_PNG:
            imagepng($srcImg, $savePath);
            break;
        case IMAGETYPE_GIF:
            imagegif($srcImg, $savePath);
            break;
    }

    // 释放资源
    imagedestroy($srcImg);
    imagedestroy($waterImg);

    return true;
}


四、使用示例

$srcImage   = './upload/photo.jpg';
$waterImage = './watermark/logo.png';

addImageWatermark($srcImage, $waterImage, './upload/photo_water.jpg', 70);


五、支持的水印位置扩展

// 左上
$dstX = 10;
$dstY = 10;

// 左下
$dstX = 10;
$dstY = $srcH - $waterH - 10;

// 右上
$dstX = $srcW - $waterW - 10;
$dstY = 10;

// 居中
$dstX = ($srcW - $waterW) / 2;
$dstY = ($srcH - $waterH) / 2;


六、PNG 水印透明度问题(重点)

❗ imagecopymerge 的坑

  • PNG 原生透明支持不好
  • 会导致水印变灰

✅ 解决方案(推荐)

使用 imagecopy + 保留 Alpha:

imagealphablending($srcImg, true);
imagesavealpha($srcImg, true);

imagecopy(
    $srcImg,
    $waterImg,
    $dstX,
    $dstY,
    0,
    0,
    $waterW,
    $waterH
);

(此方式使用 PNG 自带透明度)


七、常见问题汇总

❓ 为什么水印模糊?

  • 水印图片本身分辨率低
  • JPEG 压缩质量过低

❓ GIF 动图能加水印吗?

  • ❌ GD 不支持动图逐帧
  • ✅ 需 Imagick

❓ 图片太小怎么办?

if ($srcW < $waterW || $srcH < $waterH) {
    return false;
}


八、Imagick(更高级,可选)

$img = new Imagick('photo.jpg');
$water = new Imagick('logo.png');

$img->compositeImage($water, Imagick::COMPOSITE_OVER, 20, 20);
$img->writeImage('out.jpg');

✔ 支持 GIF
✔ 更好透明处理
❌ 扩展安装复杂


九、一句话总结

GD 够用,Imagick 更强;PNG 水印最佳;注意透明度处理