使用ImageMagick操作gif图
上篇文章我们已经学习了 GraphicsMagick 中的许多函数,也说过 GraphicsMagick 是 ImageMagick 的一个分支,所以他们很多的函数都是一样的使用方式和效果,相似的内容我们也就不再多说了,感兴趣的朋友可以直接查阅官方文档。
这篇文章我们要学习的是一个具体的案例,也是我在实际业务开发中所接触过的一个案例。具体的效果就是对于微信小游戏和小程序来说,不能直接地使用动态 Gif 图片,一张 Gif 图片在小游戏或小程序中是不会动的。所以在我们公司的游戏开发中,需要一张将整个 Gif 动图的每一帧拆出来的图片拼成一张精灵图交给前端,由他们来使用 JS+CSS 的能力动态地循环我们拆帧后的图片,从而形成动图的效果。
业务需求就是这么个情况,当然,最后的解决方案也正是使用了 ImageMagick 来实现的。话不多说,我们直接先看代码。
GIF 图拆帧
原始的图片是这样的一张动图:
$imgPath = '../img/4.gif';
$imagick = new \Imagick($imgPath);
$imagick = $imagick->coalesceImages();
$imageCount = $imagick->count();
echo 'image count:', $imageCount, PHP_EOL; // image count:51
$imgAttrs = [
'width' => $imagick->getImageWidth(),
'height' => $imagick->getImageHeight(),
'frame_count' => $imageCount,
];
$column = 5;
if ($imageCount < $column) {
$column = $imageCount;
}
$row = ceil($imageCount / $column);
$spImgWidth = $imgAttrs['width'] * $column;
$spImgHeight = $imgAttrs['height'] * $row;
// 创建图片
$spImg = new \Imagick();
$spImg->setSize($spImgWidth, $spImgHeight);
$spImg->newImage($spImgWidth, $spImgHeight, new \ImagickPixel('#ffffff00'));
$spImg->setImageFormat('png');
$i = 0;
$h = 0;
$cursor = 0;
do {
if ($i == $column) {
$i = 0;
$h++;
}
if($cursor == 0){ // 保存第一帧图片
$imagick->writeImage($imgPath . '.first.png');
}
// 保存全部的图片帧到一张 png 图片中
$spImg->compositeImage($imagick, \Imagick::COMPOSITE_DEFAULT, $i * $imgAttrs['width'], $h * $imgAttrs['height']);
$i++;
$cursor++;
} while ($imagick->nextImage());
$spImg->writeImage($imgPath . '.png');
实例化 Imagick 对象就不用多说了,我们首先调用的是 coalesceImages() 这个方法。它的作用是返回合成后的 Imagick 对象。通过这个方法,我们就获得了整个 GIF 图里面的全部每一帧图片的信息。这时,使用 count() 方法,就可以获得图片中的所有图片帧的个数。比如我们测试的这张图片就有 51 帧。
然后计算精灵图的行和列以及相应需要的宽高,比如我们以 5 列为基准,也就是一行放五张拆帧出来的图片,这样一共需要 11 行才放得下最后生成的精灵图。同理,宽高也是以拆出来的图片宽高乘以相应的列和行数。
接着,根据计算出来的宽高生成一张新的图片,作为精灵图的背景图,使用 newImage() 函数设置图片宽高及背景透明。使用 setImageFormat() 方法设置图片的格式为 PNG 格式,使用 PNG 主要是为了透明,其实按我们这样紧密排列的图片来说,不用透明也可以,但某些应用中比如网站前端需要的精灵图可能不同的图片之前是需要一定间隔的,所以一般会使用透明的底图。
然后就是一个循环,也就是循环那 51 张拆帧出来的图片,使用 nextImage() 不断地获取原始 GIF 图中的下一帧图片,并将他们组合保存在上面新建的背景图片中,每一帧的图片位置也是通过单帧图片的宽高与行列情况计算出来的。在这段代码中,我们还保存了第一帧的图片,当然,这也是业务需要,你可以随时保存任何一张每帧的图片。
最后,使用 writeImage() 保存图片。输出的图片就是下面的这个样子:
组合成动态 GIF 图
以上的业务功能是我在开发中实际使用过的功能,当然,除了可以对 GIF 图进行拆帧之外,我们也可以将多张图片组合成一个动态的 GIF 图。
$gifImagek = new Imagick();
$gifImagek->setFormat('GIF');
for($i=1;$i<=5;$i++){
$img = new Imagick('../img/3'.$i.'.jpeg');
$img->setImageDelay(100);
$gifImagek->addImage($img);
}
$gifImagek->writeImages("../img/5.gif", true);
$gifImagek->writeImages("../img/52.gif", false);
这段代码就比较简单了,依然还是创建一个图片,并且指定格式为 GIF 图片。然后循环添加图片,这里我们使用的是上篇文章中 GraphicsMagick 中操作过的那些图片。setImageDelay() 用于设置图片显示间隔,这里我们设置的是 100 毫秒,然后再使用 addImage() 将图片添加到我们新创建的 GIF 图画布中。
最后保存图片的时候,需要使用 writeImages() 进行保存,它的作用是保存这种连续的多张图片。它的第二个参数是指定是否将图片保存到一张图片中,如果是 false 的话,就类似于拆帧的效果,不过会将图片一张一张的分开保存,比如 52-1.gif 、 52-2.gif 这样。
最后生成的动图就是这样的:
总结
今天的内容有意思吧,不是那些烂大街的缩放、加水印、验证码之类的功能,而是比较好玩的对于 GIF 图的操作。说实话,在业务开发中类似的业务场景还是很多的,就像自动生成精灵图这种功能就完全可以使用 ImageMagick 来实现,而且都是 ImageMagick 扩展中自带的函数就可以搞定了,非常方便。
测试代码:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202012/source/5.使用ImageMagick操作gif图.php
参考文档:
https://www.php.net/manual/zh/book.imagick.php
关注公众号:【硬核项目经理】获取最新文章
添加微信/QQ好友:【xiaoyuezigonggong/149844827】免费得PHP、项目管理学习资料
知乎、公众号、抖音、头条搜索【硬核项目经理】
B站ID:482780532
使用ImageMagick操作gif图的更多相关文章
- MySQL 服务器变量 数据操作DML-视图
原文:MySQL 服务器变量 数据操作DML-视图 SQL语言的组成部分 常见分类: DDL:数据定义语言 DCL:数据控制语言,如授权 DML:数据操作语言 其它分类: 完整性定义语言: DDL的一 ...
- .net I/O操作 导图
稍微总结下,System.IO提供了四种类型来实现,对单个文件和计算机目录结构的操作.Directory和File通过静态成员实现建立.删除.复制和移动操作(上图没有提及).而FileInfo和Dir ...
- MONGODB 与sql聚合操作对应图
MongoDB 高级查询条件操作符 2012-04-25 15:35:19| 分类: MongoDB | 标签:mongodb使用 mongodb查询 |举报|字号 订阅 http://blo ...
- mongoDB与sql聚合操作对应图
SQL Terms, Functions, and Concepts MongoDB Aggregation Operators WHERE $match GROUP BY $group HAVING ...
- windows环境下安装pymysql(操作带图)
在windows环境下安装pymysql,首先要找到python的安装位置,如果在c盘,启动cmd的时候,要获取管理员权限. 具体步骤,一,管理员模式启动cmd.在箭头指定位置,搜索cmd,出现快捷方 ...
- SVN在idea中操作解析图
进入的位置
- Spark GraphX 属性图操作
package Spark_GraphX import org.apache.spark.graphx._ import org.apache.spark.rdd.RDD import org.apa ...
- Spark入门实战系列--9.Spark图计算GraphX介绍及实例
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .GraphX介绍 1.1 GraphX应用背景 Spark GraphX是一个分布式图处理 ...
- MySql数据库-使用cmd操作数据库
寄语: 针对一些公司对测试岗位掌握SQL的要求,本博文以此献给没有掌握数据库语句知识的功能测试人员,愿与广大测试同胞共同进步. 如果电脑上已安装配置好MySQL数据库,打开命令提示符,按照下图以此操作 ...
随机推荐
- SpringCloud升级之路2020.0.x版-14.UnderTow AccessLog 配置介绍
本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford server: u ...
- AlexNet论文总结
论文链接:https://papers.nips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf Q1:解决了什么? 目前主 ...
- SQL 练习12
查询和" 01 "号的同学学习的课程 完全相同的其他同学的信息 分析 如果某同学学的某一个课程和01同学所学的课程有对应,那么子查询返回false. 如果没有对应,子查询返回tru ...
- C++ 矩形交集和并集的面积-离散化
//离散化,x,y坐标分别按从小到大排序 //离散化 //1.首先分离出所有的横坐标和纵坐标分别按升序存入数组X[ ]和Y[ ]中. //2. 设数组XY[ ][ ].对于每个矩形(x1,y1)(x2 ...
- 【C#】 Stopwatch详解
Stopwatch的命名空间是using System.Diagnostics; 1 namespace System.Diagnostics 2 { 3 // 4 // 摘要: 5 // 提供一组方 ...
- mysql基础操作(一):DDL、DML
-- 1.在命令行中开启数据库: net start mysql -- 2.在命令行中关闭数据库: net stop mysql 1.DDL语句:create.drop.alter -- 查看所有的数 ...
- JDBC基础篇(MYSQL)——使用statement执行DML语句(insert/update/delete)
注意:其中的JdbcUtil是我自定义的连接工具类:代码例子链接: package day02_statement; import java.sql.Connection; import java.s ...
- 在多数据源中对部分数据表使用shardingsphere进行分库分表
背景 近期在项目中需要使用多数据源,其中有一些表的数据量比较大,需要对其进行分库分表:而其他数据表数据量比较正常,单表就可以. 项目中可能使用其他组的数据源数据,因此需要多数据源支持. 经过调研多数据 ...
- 5M1E,软件质量管理最佳解决方案
- 如何做好一个产品? - 用户.需求.文化.价值.设计.流程,这些因素缺一不可.- 那么,如何做好产品的质量管理?- 人.机器.物料.方法.环境.测量,这些因素同样缺一不可.能够影响产品质量波动的因 ...
- CodeReview杂谈
豆皮粉儿们,大家好,又见面啦,今天由字节跳动的"躬冯"带来一个 code review 的故事. 作者:躬冯 2020年元旦假期到来的时候,孙总攒了个局,又把当年一起创造过屎山的咱 ...