这篇文章上次修改于 269 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

还记得我曾经在博客上《PHP 生成网页缩略图的方法》文章写过的一个缩略图处理函数吗?凌晨在更新相册功能的时候偶然发现了个问题,和它有着较大的关联。

我曾经为了测试接口,上传了一份透明 .png 图片,理论上生成的缩略图只是一个裁切后带黑色背景的图,可事实是它却出现了两次原图的内容,我的缩略图生成过程必然是出现了问题。(可以看附图的第二行第二张,前面的是调试正常后生成的缩略图)

调试中的相册

// 如果原图的宽更大
if($width > $height){
    // 取得居中偏移值(大的减去小的除去两个多出来的边 = 左侧单个边的值)
    $x_offset = ceil(($width - $height) / 2);
    // 新图,原图,X 偏移(在新图上的),Y 偏移(在新图上的),X 偏移(在旧图上准备采取的),Y 偏移(在旧图上准备采取的),在旧图上采取的宽度,高度(裁切范围)
    imagecopy($image, $image, 0, 0, $x_offset, 0, $height, $height);
    // 仅被用于缩小图片,$size 为目标裁切尺寸
    imagecopyresampled($new, $image, 0, 0, 0, 0, $size, $size, $height, $height);
}

简单分析下我之前的处理逻辑,其实就是先使用 imagecopy 函数取得原图的一个方块区域,之后粘贴在原图上左上角的位置(x=0, y=0),接着再调用 imagecopyresampled 函数取得原图那个被粘贴的方块区域,同时缩小画布尺寸,粘贴在新画布 $new 上,最终存盘。很明显这个思路就存在了重复的过程,因为在原图里粘贴了一次,因此使得输出结果中出现了两次原图内容的错误表现。

一番理解后,其实 imagecopyimagecopyresampled 两个函数都有着复制粘贴的相同用途,但是 imagecopyresampled 多了两个参数,可以用于缩放图片。这就不难解释为什么之前的写法即便是用 imagecopy 处理偏移,也不忘记用 imagecopyresampled 缩小了。修改后的函数是这样的:

// 如果原图的宽更大
if($width > $height){
    // 取得居中偏移值(大的减去小的除去两个多出来的边 = 左侧单个边的值)
    $x_offset = ceil(($width - $height) / 2);
    // 新图,原图,X 偏移(在新图上的),Y 偏移(在新图上的),X 偏移(原图上的),Y 偏移(原图上的),缩小后的宽和高,在原图上采取的宽和高(定义采取区域)
    imagecopyresampled($new, $image, 0, 0, $x_offset, 0, $size, $size, $height, $height);
}

说简单一点,就是直接从 $image 原图利用 $x_offset, 0 偏移和 $height, $height 范围取得一个方块采取区域,然后平滑缩小到 $size, $size 尺寸,并粘贴到新画布 $new 里面,整个处理过程就结束了。

经过以上的分析与推理,这个函数的 Bug 就得到了修复和解决了。