MrZyb Always in fear of one's own ignorance
  • 第一种方法:
<?php
function createImg($srcFile) {
    $data = getimagesize($srcFile);
    switch ($data['2']) {
        case 1:
            $im = imagecreatefromgif($srcFile);
            break;
        case 2:
            $im = imagecreatefromjpeg($srcFile);
            break;
        case 3:
            $im = imagecreatefrompng($srcFile);
            break;
        case 6:
            $im = imagecreatefromwbmp($srcFile);
            break;
    }
    return $im;
}

/**
 * 图片圆角处理函数
 *
 * @param source  $srcImg GD图片资源
 * @param integer $radius 圆角大小 = 最短边的长度 / $radius
 */
function roundImgs($srcImg, $radius=2) {
    $w = imagesx($srcImg);
    $h = imagesy($srcImg);
    $radius = min($w, $h) / $radius;
    $img = imagecreatetruecolor($w, $h);
    imagesavealpha($img, true); // 设置透明通道
    $bg = imagecolorallocatealpha($img, 255, 255, 255, 127); // 拾取一个完全透明的颜色, 最后一个参数127为全透明
    imagefill($img, 0, 0, $bg);
    $r = $radius; // 圆 角半径
    for ($x = 0; $x < $w; $x++) {
        for ($y = 0; $y < $h; $y++) {
            $rgbColor = imagecolorat($srcImg, $x, $y);
            if (($x >= $radius && $x <= ($w - $radius)) || ($y >= $radius && $y <= ($h - $radius))) {
                imagesetpixel($img, $x, $y, $rgbColor); // 不在四角的范围内,直接画
            } else { // 在四角的范围内选择画
                // 上左
                $yx = $r; // 圆心X坐标
                $yy = $r; // 圆心Y坐标
                if (((($x - $yx) * ($x - $yx) + ($y - $yy) * ($y - $yy)) <= ($r * $r))) {
                    imagesetpixel($img, $x, $y, $rgbColor);
                }
                // 上右
                $yx = $w - $r; // 圆心X坐标
                $yy = $r; // 圆心Y坐标
                if (((($x - $yx) * ($x - $yx) + ($y - $yy) * ($y - $yy)) <= ($r * $r))) {
                    imagesetpixel($img, $x, $y, $rgbColor);
                }
                // 下左
                $yx = $r; // 圆心X坐标
                $yy = $h - $r; // 圆心Y坐标
                if (((($x - $yx) * ($x - $yx) + ($y - $yy) * ($y - $yy)) <= ($r * $r))) {
                    imagesetpixel($img, $x, $y, $rgbColor);
                }
                // 下右
                $yx = $w - $r; // 圆心X坐标
                $yy = $h - $r; // 圆心Y坐标
                if (((($x - $yx) * ($x - $yx) + ($y - $yy) * ($y - $yy)) <= ($r * $r))) {
                    imagesetpixel($img, $x, $y, $rgbColor);
                }
            }
        }
    }
    return $img;
}

function cropImg($img, $axisx, $axisy) {
    $width = imagesx($img);
    $height = imagesy($img);
    $setRatio = $axisx / $axisy;
    $curRatio = $width / $height;
    if ($setRatio > $curRatio) {
        $resizeX = $width;
        $resizeY = $resizeX * $axisy / $axisx;
        $x = 0;
        $y = ($height - $resizeY) / 2;
    } elseif ($setRatio < $curRatio) {
        $resizeY = $height;
        $resizeX = $resizeY * $axisx / $axisy;
        $x = ($width - $resizeX) / 2;
        $y = 0;
    } else {
        $resizeX = $width;
        $resizeY = $height;
        $x = $y = 0;
    }
    $im = imagecreatetruecolor($resampleX, $resampleY);
    imagecopyresampled($im, $img, 0, 0, $x, $y, $resampleX, $resampleY, $resampleX, $resampleY);
    imagedestroy($img);
    return $im;
}

$url = 'https://upload-images.jianshu.io/upload_images/8396841-829819a83d4a77e4.jpg';
$img = createImg($url); // 创建GD图像资源
$img = cropImg($img, 1, 1); // 为了美观居中裁剪成正方形
$img = roundImgs($img); // 将四个角画成透明的
imagepng($img, './roundBirds.png');
imagedestroy($img);

原图裁剪后

  • 第二种方法:使用PHP Imagick库进行图片圆角处理,Imagick是自带的图片圆角处理,在执行效率上也比GD的要胜上一筹。
    /**
    * Imagick裁剪圆形图片
    *
    * @param strimg $imgPath 图片路径
    * @param string $saveName 保存路径
    * @return source 生成的图片
    */
    function roundImg($imgPath, $saveName){
    $image = new Imagick($imgPath);
    $image->setImageFormat('png');
    $width = $image->getImageWidth();
    $height = $image->getImageHeight();
    $image->roundCorners($width/2, $height/2);
    $saveName = $saveName ? : substr(md5(time()), 0, 5).'.png';
    $image->writeImage($saveName);
    $image->destroy();
    }
  • 第三种方法:使用PHP GD库进行处理。速度:快。缺点:imagecopyresampled时透明部分会失真,要缩放只能用imagecopyresesize
/**
 * 处理圆角图片
 * @param  srting $imgpath 源图片的路径
 * @return [type]          [description]
 */
function roundedCorners($imgpath){
    list($width,$height,$type) = getimagesize($imgpath);//获取上传图片大小
    if ($width != $height) {//如果上传图片不是正方形,取最小宽度作为最终生成图的大小
        if ($width > $height) {
            $imsize = $height;
        } else {
            $imsize = $width;
        }
    }
    $im = imagecreatetruecolor($imsize, $imsize);//这里创建第一个图像
    $white = imagecolorallocate($im, 255, 255, 255);// 随便取两个颜色,这里取黑色和白色
    $black = imagecolorallocate($im, 0, 0, 0);
    imagefill($im, 0, 0, $white);//将图片填充为白色
    imagefilledellipse($im, $imsize/2, $imsize/2, $imsize, $imsize, $black);//然后再图片中间画一个黑色的圆
    imagecolortransparent($im, $black);//将黑色设为透明的,则现在四个角是白色的,然后中间是透明的
    switch ($type) {
        case '2':
            $img = imagecreatefromjpeg($imgpath);//这里创建的是第二个图像
            break;
        default:
            $img = imagecreatefrompng($imgpath);//这里创建的是第二个图像
            break;
    }
    $final = imagecreatetruecolor($imsize, $imsize);//再创建一个图像,第三个图像
    imagecopyresampled($final, $img, 0, 0, ($width-$imsize)/2, ($height-$imsize)/2, $imsize, $imsize, $imsize, $imsize);//先将第二个图像(图片)压在空白的图像上,根据最小宽度,居中裁剪,成为第四个图像
    imagecopymerge($final, $im, 0, 0, 0, 0, $imsize, $imsize, 100);//再将第一个图像压在第四个图像上,由于中间是透明的,所以现在图像上中间是图片,四个角都是白色的,第五个图
    imagecolortransparent($im, $white);//然后将白色设置为透明的,现在这个图四个角是透明的,然后中间是黑色的
    imagecopymerge($im, $final, 0, 0, 0, 0, $imsize, $imsize, 100);//将第五个图压在最后的图像上,就可以得到最后的圆形的图了
    return $im;//返回图片
}
  • 要裁剪不规则图形则需要借助另外的图片来完成任务。需要借助二值图作为模板图,或者给出的模板图片上,要抠出来的部分须为不透明的,要去掉的部分则为透明的,传入模板图片和原始图片地址,返回一个图片资源。
/**
 * 根据给出的模板抠图
 * @param  string $templateImgUrl 模板图片地址
 * @param  string $userImgUrl     原始图片地址
 * @return source $img         图片资源
 */
function createImg($srcFile) {
    $data = getimagesize($srcFile);
    switch ($data['2']) {
        case 1:
            $im = imagecreatefromgif($srcFile);
            break;
        case 2:
            $im = imagecreatefromjpeg($srcFile);
            break;
        case 3:
            $im = imagecreatefrompng($srcFile);
            break;
        case 6:
            $im = imagecreatefromwbmp($srcFile);
            break;
    }
    return $im;
}

function cropImgByTemplate($templateImgUrl, $userImgUrl) {
    list($w, $h) = getimagesize($userImgUrl);
    $uimg = createImg($userImgUrl);
    $tempImg = imagecreatefrompng($templateImgUrl);
    list($width,$height) = getimagesize($templateImgUrl);
    $uim = imagecreatetruecolor($width, $height); // 将用户图片的尺寸大小缩放到模板的大小方便裁剪
    imagecopyresampled($uim, $uimg, 0, 0, 0, 0, $width, $height, $w, $h);
    $img = imagecreatetruecolor($width, $height); // 创建一个底图,大小和模板一致
    imagesavealpha($img, true); // 设置alpha通道为true,显示透明部分
    $bg = imagecolorallocatealpha($img, 255, 255, 255, 127); // 摄取一个透明的颜色
    imagefill($img, 0, 0, $bg); // 将透明颜色填满底图,得到一张全透明的图片

    for ($i=0; $i < $width; $i++) { // 遍历图片的像素点
        for ($j=0; $j < $height; $j++) {
            $rgb = imagecolorat($tempImg, $i, $j); // 获取模板上某个点的色值
            if (!$rgb) { // 不是透明的
                $color = imagecolorat($uim, $i, $j); // 在用户的图片上对应的地方取色值
                imagesetpixel($img, $i, $j, $color); // 在透明底图上画出要裁剪出来的部分
            }
        }
    }
    return $img;
}

$tempUrl = 'https://upload-images.jianshu.io/upload_images/8396841-50c151628048a0cf.png';
$url = 'https://upload-images.jianshu.io/upload_images/8396841-829819a83d4a77e4.jpg';
$img = cropImgByTemplate($tempUrl, $url);
imagepng($img, './test.png');
imagedestroy($img);

模板图原图裁剪结果

0.002424s