一起学习PHP中GD库的使用(三)

上篇文章我们已经学习了一个 GD 库的应用,那就是非常常用的制作验证码的功能。不过在现实的业务开发中,这种简单的验证码已经使用得不多了,大家会制作出更加复杂的验证码来使用。毕竟现在的各种外挂软件已经能够轻松地破解这种简单的图片验证码了。当然,我们也可以简单地对他进行变形,比如使用中文然后按顺序点击之类的,这些都比较简单地就能实现。更复杂的验证码则推荐使用一些开源的库或者api来实现。

今天,我们将继续学习 GD 库的一些常用的应用。依然是通过一些小例子来进行学习,同样也是我们在日常开发中非常常用的一些功能。

生成缩略图

在日常的开发过程中,不管是客户还是我们自己在后台上传的图片,大小可能都不一定是我们需要的尺寸,这个时候缩略图的功能就比较重要了。一般我们会在保留原图的基础上生成对应原图的一张缩略图用于前台统一尺寸页面的展示。

$w = imagesx($im);
$h = imagesy($im);

$imNew = imagecreatetruecolor($w / 2, $h / 2);

imagecopyresized($imNew, $im, 0, 0, 0, 0, $w / 2, $h / 2, $w, $h);

header("Content-type: image/jpg");
imagejpeg($imNew);
imagedestroy($imNew);

上述代码中,我们生成的缩略图是原图的一半大小,使用的就是 imagecopyresized() 这个函数,它的参数依次是新图画布、原图、新图的x和y坐标起始点、原图的x和y坐标起始点、新图的大小、原图的大小。参数比较多,但也比较好理解,就是将原图缩小到指定的大小并放到新的画布上就可以了。

https://upload-images.jianshu.io/upload_images/10153099-54ca3a5303453d02.png

imagesx() 和 imagesy() 函数不要从字面理解为什么 x 、 y 坐标点之类的,它们其实是获得图像句柄文件的宽和高。如果我们输出的是 jpg 格式的图片,还可以指定它的压缩比率。

$w = imagesx($im);
$h = imagesy($im);

$imNew = imagecreatetruecolor($w / 2, $h / 2);

imagecopyresized($imNew, $im, 0, 0, 0, 0, $w / 2, $h / 2, $w, $h);

header("Content-type: image/jpg");
imagejpeg($imNew, null, 10);
imagedestroy($imNew);

也就是 imagejpeg() 函数的最后一个参数,就和 PS 导出图片时的压缩比率一样,如果数字越小,压缩比越高,数字越大,压缩比越低,图片质量也就越好。默认值为 75 ,可以设置从 0 到 100 的压缩比。第二个参数依然是保存图片的路径,我们这里测试的代码还是直接从浏览器输出的,所以我们这里是给的一个 null 。

https://upload-images.jianshu.io/upload_images/10153099-c61e7221383733cb.png

从图片的画质来看,确实比上一张直接缩小的图片模糊了许多。当然,图片的大小也小了很多。对于网站的优化来说,jpg 图片的压缩比例一般都会在默认值的 75 左右。如果太小就会出现这种过于模糊的情况从而影响用户的体验。具体业务具体分析,需要多大的图片大小还是要根据我们实际的情况来定。

生成指定大小的等比例缩略图

还有一种业务情况是,我们前台的图片展示大小都是一样的,比如商品图片在列表中的显示。这时,很多图片直接压缩可能就会丢失比例,比如我们上传了一张 16:9 的大宽图,而前台列表页的图片位置是 4:3 的图,这里我们就要等比例按照最大宽度或者最大高度进行缩小,同时多出来的部分留白边或者透明边,这时,只要计算一下图片的比例情况就可以了。

$w = imagesx($im);
$h = imagesy($im);

$imNew = imagecreatetruecolor(202, 152);
imagefill($imNew, 0, 0, imagecolorallocate($imNew, 255, 255, 255));
imagerectangle($imNew, 0, 0, 201, 151, imagecolorallocate($imNew, 0, 0, 0));

$sW = 0;
$sH = 0;
if ($w / $h > 200 / 150) {
    $q = 200 / $w;
    $sH = $h * $q;
    $sW = $w * $q;
    $sX = 0;
    $sY = (150 - $sH) / 2;
} else {
    $q = 150 / $h;
    $sH = $h * $q;
    $sW = $w * $q;
    $sX = (200 - $sW) / 2;
    $sY = 0;
}

imagecopyresized($imNew, $im, $sX + 2, $sY + 1, 0, 0, $sW, $sH, $w, $h);

header("Content-type: image/jpg");
imagejpeg($imNew);
imagedestroy($imNew);

在测试代码中,我们规定的大小是 200*150 的图片大小,也就是 4:3 的图片规格。而需要操作的图片则是 300*244 的一张不太规范的图片。这时,我们通过计算 宽/高 的比例,来确定是以宽为基准进行缩小还是以高为基准进行缩小。如果原图的宽高比大于我们规定的图片宽高比,则认为是以宽度为基准进行缩小。反之,就是以高度进行缩小。同样地,具体的宽高结果的算法都都是基于对应的比率进行等比例缩小的。同时,我们还要计算图片的位置,要放在居中的位置。最后,再将缩小的大小放入到指定大小的画布中。

https://upload-images.jianshu.io/upload_images/10153099-5ac882e09ccd069d.png

我们这段测试代码中的画布多了两个像素,是为了画那个黑色的边框,目的也是为了演示能够看清楚。

可以看到,我们等比例缩放之后是以原图的高为基准进行缩放的,所以图片的两边会出现白边。如果是以宽为基准的,那么图片上下会出现白边。当然,如果原图的比例和我们需要的比例是一样的,就会完整地撑满整个画布。大家可以自己用其它大小的图片测试一下。

图片加水印

除了缩略图之外,加水印的功能也是很多业务开发中必备的功能。直接的文字水印其实就不用多说了,上篇文章中的 imagettftext() 就可以直接加了,只需要给它用 imagecolorallocatealpha() 函数指定一个带透明的颜色就可以了。今天我们主要来讲的是图片水印的添加。

$imNew = imagecreatetruecolor(150, 30);

imagecolortransparent($imNew, imagecolorallocatealpha($imNew, 255, 255, 255, 128));
imagesavealpha($imNew, true);

$font = '../font/msyh.ttf';
imagettftext($imNew, 16, 0, 11, 21, imagecolorallocate($imNew, 255, 255, 255), $font, '硬核项目经理');

if (imagesx($im) > 150 + 10 && imagesy($im) > 60 + 10) {
    imagecopy($im, $imNew, imagesx($im) - 150 - 10, imagesy($im) - 30 - 10, 0, 0, 150, 30);

    imagecopymerge($im, $imNew, imagesx($im) - 150 - 10, imagesy($im) - 60 - 10, 0, 0, 150, 30, 50);
}

header("Content-type: image/jpg");
imagejpeg($im);
imagedestroy($im);

首先,我们通过 imagecolortransparent() 和 imagesavealpha() 指定一个透明画布。然后通过 imagettftext() 生成一张文字图片。注意,这里是图片哦,不是直接添加的文字。

接着,使用 imagecopy() 或 imagecopymerge() 来将水印图片拷贝到原始图片上。这两个函数的区别就是 imagecopymerge() 在图片合并的时候多了一个参数可以指定通道的透明度,也就是说,如果是一张不带透明度的图片可以直接使用这个函数来让图片增加透明的效果。

在添加水印之前的判断是用于判断图片大小是否适合添加水印,如果图片比水印文件还小的话,那么就不要添加水印了,或者再将水印也缩小后再进行添加。

https://upload-images.jianshu.io/upload_images/10153099-cbceccaf68071cbc.png

这样,简单地水印添加就完成了。网上其实能找到很多前辈已经封装好的添加水印的类,或者 Composer 中也有很多现成的库,这里只是手写一个简单的效果供大家学习复习。

总结

关于图片 GD 库的功能函数还有很多,但说实话,笔者现在都已经用得不多了。为什么呢?在实际的业务开发中,大家其实都已经习惯使用 oss 、七牛、upyun 之类的云存储了。不管是图片缩放、添加水印,甚至是简单地进行一些 PS 编辑,都非常方便。而且最主要的是不需要再占用我们的服务器存储资源以及带宽资源,何乐而不为呢。像我现在的工作中,程序代码服务器基本上只需要原始的 20G 左右大小就可以了,只是运行代码,不存储上传的文件、图片以及静态资源。

测试代码:

https://github.com/zhangyue0503/dev-blog/blob/master/php/202012/source/3.一起学习PHP中GD库的使用(三).php

参考文档:

https://www.php.net/manual/zh/book.image.php

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容