不败君

前端萌新&初级后端攻城狮

原生PHP使用GD库压缩图片及增加水印

原生PHP使用GD库压缩图片及增加水印

2020-08-06 18:28:00

围观(3691)

压缩图片

图片压缩其实很多场景都会用得上, 著名的有 tinypng 压缩服务.

博主之前也经常用 tinypng 但是感觉有点限制.

所以试着用 GD 库简单实现一下压缩图片, 当然这样的压缩是有损的.

直接上博主已经写好的代码:

/**
 * @param $params
 * ['photo']        // 图片地址
 * ['quality']      // 图片压缩后的质量 值范围: 1 - 75 默认值 50
 * ['like_width']   // 偏好宽度, 如果图片宽度大于该宽度值 会自动压缩为该宽度的数字大小 默认为 900PX
 * ['cover']        // 是否覆盖原图 0为不覆盖 1为覆盖
 * @return false|string
 */
function compressPicture($params)
{
    $gd_info = gd_info();
    if (empty($gd_info['GD Version'])) exit('请安装GD库');

    if (empty($params['photo'])) exit('请输入图片地址');

    if (!file_exists($params['photo'])) exit('图片文件不存在');

    $quality = $params['quality'] ?? 50;

    if ($quality < 1 || $quality > 75) exit('压缩质量错误');

    $like_width = $params['like_width'] ?? 900;

    // 获取图片信息(高度和宽度)
    $photo_file = file_get_contents($params['photo']);
    $photo_size = getimagesizefromstring($photo_file);
    $photo_width = $photo_size[0];
    $photo_height = $photo_size[1];

    $new_width = $photo_width;
    $new_height = $photo_height;

    // 使用原图创建新画布
    switch ($photo_size['mime']){
        case 'image/jpeg':
            $image = imagecreatefromjpeg($params['photo']);
            break;
        case 'image/gif':
            $image = imagecreatefromgif($params['photo']);
            break;
        case 'image/png':
            $image = imagecreatefrompng($params['photo']);
            break;
    }

    if (!isset($image)) exit('请上传图片或图片格式错误');

    if ($photo_width > $like_width) {
        // 裁剪图片尺寸
        $new_width = $like_width;
        $proportion = $like_width / $photo_width;
        $new_height = $proportion * $photo_height;
    }

    $new_image = imagecreatetruecolor($new_width, $new_height);
    if ($photo_size['mime'] == 'image/png') {
        // 解决 PNG 格式图片透明背景
        $color = imagecolorallocate($new_image,255,255,255);    // 为一幅图像分配颜色
        imagecolortransparent($new_image, $color);                              // 设置透明
        imagealphablending($new_image, false);                      // 设定图像的混色模式
        imagesavealpha($new_image, true);                       // 设置标记以在保存 PNG 图像时保存完整的 alpha 通道信息
    }

    imagecopyresampled($new_image, $image, 0, 0, 0, 0, $new_width, $new_height, $photo_width, $photo_height);

    $ext = pathinfo($params['photo'],PATHINFO_EXTENSION);

    $new_photo_name = md5(time() . mt_rand(1000, 9999)) . '.' . $ext;

    if (!empty($params['cover'])) {
        // 覆盖原图
        $new_photo_name = $params['photo'];
    }

    switch ($photo_size['mime']){
        case 'image/jpeg':
            imagejpeg($new_image, $new_photo_name, $quality);
            break;
        case 'image/gif':
            imagegif($new_image, $new_photo_name);
            break;
        case 'image/png':
            imagepng($new_image, $new_photo_name);
            break;
    }

    // 销毁资源
    imagedestroy($image);
    imagedestroy($new_image);

    return json_encode([
        'code' => 0,
        'url' => $new_photo_name,
    ]);
}


可以在方法外面增加一个判断:

if ($_GET['action'] == 'compress_picture') {
    // 图片压缩
    exit(compressPicture($_GET));
}


接着可以用 Postman 或者浏览器直接请求测试. 博主用了一张 3M 左右的 JPG 格式图片进行测试:

1.png


进行请求压缩:

2.png


此时再查看该图片的大小:

3.png


增加水印

博主写的方法:

/**
 * @param $params
 *
 * 水印所需参数:
 * ['photo']        // 图片地址
 * ['content']      // 水印内容(如果是文字水印 则直接传文字内容 如果是图片水印 传图片路径)
 * ['x']            // 水印所在位置的 X 轴坐标
 * ['y']            // 水印所在位置的 Y 轴坐标
 * ['type']         // 1图片水印 2文字水印
 * ['cover']        // 是否覆盖原图 0为不覆盖 1为覆盖 
 * 
 * @return false|string
 */
function addWatermark($params)
{
    $gd_info = gd_info();
    if (empty($gd_info['GD Version'])) exit('请安装GD库');

    if (empty($params['photo'])) exit('请输入图片地址');

    if (!file_exists($params['photo'])) exit('图片文件不存在');

    if(empty($params['content'])) exit('缺少水印内容');

    $x = 10;
    $y = 30;
    if (!empty($params['x']) && is_numeric($params['x'])) $x = $params['x'];
    if (!empty($params['y']) && is_numeric($params['y'])) $y = $params['y'];

    // 获取图片信息(高度和宽度)
    $photo_file = file_get_contents($params['photo']);
    $photo_size = getimagesizefromstring($photo_file);
    $photo_width = $photo_size[0];
    $photo_height = $photo_size[1];

    // 使用原图创建新画布
    switch ($photo_size['mime']){
        case 'image/jpeg':
            $image = imagecreatefromjpeg($params['photo']);
            break;
        case 'image/gif':
            $image = imagecreatefromgif($params['photo']);
            break;
        case 'image/png':
            $image = imagecreatefrompng($params['photo']);
            break;
    }

    if (!isset($image)) exit('请上传图片或图片格式错误');

    $new_image = imagecreatetruecolor($photo_width, $photo_height);
    if ($photo_size['mime'] == 'image/png') {
        // 解决 PNG 格式图片透明背景
        $color = imagecolorallocate($new_image,255,255,255);    // 为一幅图像分配颜色
        imagecolortransparent($new_image, $color);                              // 设置透明
        imagealphablending($new_image, false);                      // 设定图像的混色模式
        imagesavealpha($new_image, true);                       // 设置标记以在保存 PNG 图像时保存完整的 alpha 通道信息
    }

    imagecopyresampled(
        $new_image,
        $image,
        0,
        0,
        0,
        0,
        $photo_width,
        $photo_height,
        $photo_width,
        $photo_height
    );

    // 增加图片水印
    if ($params['type'] == 1) {
        $watermark_info = getimagesize($params['content']);

        // 获取水印图片的类型
        $watermark_type = image_type_to_extension($watermark_info[2], false);

        // 在内存创建图像
        $watermark_create_image_function = "imagecreatefrom{$watermark_type}";
        // 把水印图片复制到内存中
        $watermark = $watermark_create_image_function($params['content']);

        // 特别处理设置透明
        // $color = imagecolorallocate($watermark, 255, 255, 255);
        // imagefill($watermark, 0, 0, $color);
        // imagecolortransparent($watermark, $color);

        // 合并图片
        imagecopymerge($new_image, $watermark, $x, $y, 0, 0, $watermark_info[0], $watermark_info[1], 100);
    }

    // 增加文字水印
    if ($params['type'] == 2) {
        // 字体颜色默认值
        $red = $params['red'] ?? 255;
        $green = $params['green'] ?? 255;
        $blue = $params['blue'] ?? 255;
        $alpha = $params['alpha'] ?? 0;

        if (!is_numeric($red) || !is_numeric($green) || !is_numeric($blue) || !is_numeric($alpha)) exit('字体颜色错误');

        $color = imagecolorallocatealpha($new_image, $red, $green, $blue, $alpha);  // 设置字体颜色和透明度

        // 字体大小
        $font_size = $params['font_size'] ?? 30;

        $font_path = __DIR__ . '/wen.ttf';

        // 写入文字
        imagettftext($new_image, $font_size, 0, $x, $y, $color, $font_path, $params['content']);
    }

    $ext = pathinfo($params['photo'],PATHINFO_EXTENSION);

    $new_photo_name = md5(time() . mt_rand(1000, 9999)) . '.' . $ext;

    if (!empty($params['cover'])) {
        // 覆盖原图
        $new_photo_name = $params['photo'];
    }

    switch ($photo_size['mime']){
        case 'image/jpeg':
            imagejpeg($new_image, $new_photo_name);
            break;
        case 'image/gif':
            imagegif($new_image, $new_photo_name);
            break;
        case 'image/png':
            imagepng($new_image, $new_photo_name);
            break;
    }

    // 销毁资源
    imagedestroy($image);
    imagedestroy($new_image);

    return json_encode([
        'code' => 0,
        'url' => $new_photo_name,
    ]);
}


在方法外面继续增加一个判断:

if ($_GET['action'] == 'add_watermark') {
    // 图片加水印
    exit(addWatermark($_GET));
}


文字水印测试:

4.png


加上水印后的效果:

5.png


图片水印测试:

6.png


加上水印后的效果:

7.png


注意

如果认真查看本文的代码, 可以看到两个方法里面重复的代码是可以优化的, 特别是两个方法的开头, 具体优化博主就不写了.


还有就是方法里面用了很多判断并 exit 结束运行, 如果是想要弄得更完善, 可以修改为返回 JSON.


关于文字水印的字体, 博主用的是站酷的免费字体, 看介绍是可以免费商用的.

站酷的字体: www.zcool.com.cn/special/zcoolfonts

本文地址 : bubaijun.com/page.php?id=204

版权声明 : 未经允许禁止转载!

评论:我要评论
发布评论:
Copyright © 不败君 粤ICP备18102917号-1

不败君

首 页 作 品 微 语