Qt 中图像相关的类主要是 QPixmap 和 QImage,QPixmap 没有提供访问图像像素数据的接口,访问图像的像素数据需要使用 QImage,主要的函数有 (相关重载函数没有列出来):
1 | // 获取图像的像素数据 |
下面把一个图像转为灰度图为例介绍怎么操作图像的像素:
- 取得图像的宽、高
- 根据宽、高遍历每一个像素
- 得到每一个像素的 RGBA 颜色分量
- 对得到的颜色分量 RGBA 进行灰度计算得到新的颜色
- 使用计算得到的颜色设置对应像素
1 | QImage gray(QImage image) { |
参数为 QImage 而不是 const QImage &,使用了 Qt 的隐式共享特性,当 QImage 的内容变化后会复制创建一个新的 QImage,不会影响原来的图像。
操作图像的像素数据是不是一点也不难,接下来实现红色蒙版效果,让图像呈现一种偏红的效果,算法是将红色通道设为红、绿、蓝三个值的平均值,而将绿色通道和蓝色通道都设为 0,参考上面的代码,很快就完成了:
1 | QImage red(QImage image) { |
接下来大家自己实现一下亮度效果,让图像变得更亮或更暗,算法是将红色通道、绿色通道、蓝色通道,同时加上一个正值或负值。有了上面的经验,相信我们很快就能写出来。
到此我们虽然学会了操作图像的像素实现各种效果,但是仔细观察它们的代码,会发现 [1], [2], [3], [5] 部分的代码都是重复的,只有实现不同效果的部分 [4] 不同。
代码重用一直是不变的主题,对于上面的实现,可以把不变的部分封装到一个函数中,变化的部分使用函数指针传进来,实现代码重用,其实这就是使用了设计模式中的策略模式,优化后的代码如下:
函数 map 封装了不变的部分
map 不是地图,而是映射的意思,把一个像素的颜色映射成另一个颜色
实现图像特效的部分使用 lambda 函数作为参数传给函数 map
1 | void map(QImage *image, std::function<QRgb (int r, int g, int b, int a)> process) { |
使用函数 map,灰度效果和红色蒙版效果的代码精简如下:
1 | QImage gray(QImage image) { |
实现高亮效果的代码则如下:
1 | QImage brightness(QImage image, int delta) { |
使用策略模式后,实现各种效果的代码变的非常简短、漂亮了很多,也减少了出错的几率,非常有价值。
最后附上图像效果的实现类 ImageEffects
,希望大家可以把它完善的更好,增加更多的效果:
1 | // 文件名: ImageEffects.h |
1 | // 文件名: ImageEffects.cpp |
使用就很直接了,例如使用 QComboBox 切换不同的效果:
1 | connect(ui->comboBox, QOverload<const QString &>::of(&QComboBox::currentIndexChanged), [this] (const QString &text) { |