Qt 用画笔对图片实现马赛克效果

最近接了一个用 Qt 做跨平台截图工具的任务,主要功能有截图、绘制图案、马赛克、毛玻璃、文字能效果,其中马赛克功能时参考网上的文献并自己研究制作出来的,这里特意给大家分享一下。有需要的朋友可以作为借鉴。

实现原理

首先要开始实现之前,我们首先要来说一下我自己实现马赛克功能的原理。

  1. 为了可以执行撤销操作,我们不能直接对图片进行修改。将图片附加到窗口上以后,需要在窗口上覆盖一层透明的窗口,在这个窗口使用画笔等工具来绘制马赛克,覆盖后面的图片来实现一个视觉效果,最终保存的时候将图片和透明窗口上绘制的数据合并得到一个绘制了马赛克效果的图片。
  2. 对窗口进行区块划分,比如当我们想让每个马赛克的块大小是 10px,那么我们以图片左上角为 0,0 点,给图片划分成一块一块 10px*10px 的小块。
  3. 当鼠标点击透明窗口的某个区域时要到实际后面图片的响应坐标取这个坐标对应像素的颜色值(QColor),如果条件允许可以将这个坐标周围的色值也都取出来做一个颜色混合得到平均的颜色色彩。
  4. 计算当前坐标对应的 10px10px 小块的起始点坐标,然后根据已经取出来的颜色绘制一个 10px10px 像素的方块。
  5. 鼠标移动过程中时时计算坐标,如果当前鼠标所在坐标已经绘制了一个图形那么不再绘制,如果没有绘制则重复 3、4 步。

大体步骤就是这样的,接下来我们来拆分每一个步骤的实现方式。

实现细节

这个透明窗口派生于 QWidget 类,在构造函数中设置窗口为透明的,这样在我们将绘制的马赛克和图片混合的时候就不会有窗口背景色了。

// 让窗口背景透明,在获取窗口绘制的数据时不显示背景色
setAttribute(Qt::WA_TranslucentBackground, true);

对窗口划分区域并根据鼠标得出马赛克绘制方块。在绘制前你要监听鼠标在窗口上按下、弹起、移动等消息,在鼠标按下前,我们要先初始化一个 image 对象来提供我们获取颜色,并根据图片大小初始化一个坐标数组,比如我们有一个 100*100 的图片,想让马赛克的大小是 10 像素,那么我们就要将这个图片宽和高都划分为 10 个 10 像素的小方块。记录下所有图片的像素坐标点。

// 将截取的未知转为 iamge 对象,用来获取坐标像素的颜色
image_ = originPainting_->toImage();
// 初始化每个像素点的坐标位置,鼠标移动过程中记录坐标并通过数组下标
// 直接访问到数据查看某个区块是否已经被绘制上了马赛克。
pointArray.reset(new QVector<QVector<bool>>);
pointArray->resize(parentWidget()->width());
for (int i = 0; i < parentWidget()->width(); i++) {
    // 高度像素
    (*pointArray)[i].resize(parentWidget()->height());
}
for (int i = 0; i < parentWidget()->width(); i++) {
    for (int j = 0; j < parentWidget()->height(); j++) {
        (*pointArray)[i][j] = false;
    }
}

在按下的时候根据背景图像取像素的点,计算并记录像素坐标点归属那一块我们划分出来的小方块:

int x = endPoint_.x() - endPoint_.x() % mosaicSize_;
int y = endPoint_.y() - endPoint_.y() % mosaicSize_;

if (x < 0) x = 0;
if (y < 0) y = 0;

// 防止越界崩溃
if (x > pointArray->size() - mosaicSize_ ||
    y > (*pointArray)[0].size() - mosaicSize_) {
    break;
}

if ((*pointArray)[x][y] == false) {
    (*pointArray)[x][y] = true;

    MosaicData mosaic;

    // 为适配 retina 屏幕从全屏界面中取像素颜色
    QColor color = image_.pixel(realX, realY);

    QPen pen;
    pen.setColor(color);

    mosaic.color_ = color;
    mosaic.pen_ = pen;
    mosaic.point_ = { x, y };

    drawMosaic_.push_back(mosaic);
}

鼠标移动的时候同样走上面的流程,记录下鼠标移动的所有的坐标。此时在透明窗口的 paintEvent 中,你已经可以根据 drawMosaic_ 里面记录的坐标开始绘制一个一个 mosaicSize_ 大小的小方块了。最后在保存图片的时候,你可以先将透明窗口图片保存为一个 pixmap,然后将 pixmap 合并到图片中就可以了。

QRect imageRect = CommonHelper::getRetinaRect(currentRect_);

// 创建一副空背景图片
QImage resultImg = QImage(imageRect.width(), imageRect.height(), QImage::Format_ARGB32);
std::shared_ptr<QPainter> painter;
painter.reset(new QPainter(&resultImg));

// 先绘制截图的内容
painter->drawPixmap(QRect(0, 0, imageRect.width(), imageRect.height()), *originPainting_, imageRect);

// 再绘制图形数据
for (auto window : drawItemList_) {
    // 跳过空文本窗体
    if (window->getDrawModel() == DrawUnit::MODEL::Text && window->getDrawText().size() == 0) {
        continue;
    }

    // 获取窗口数据到 pixmap 中
    QPixmap pixmap = window->grab();
    // 获取窗口实际大小
    QRect windowRect = window->rect();
    // retina 兼容
    QRect windowImageRect = CommonHelper::getRetinaRect(windowRect);

    painter->drawPixmap(QRect(currentRect_.width() - windowRect.width(), currentRect_.height() - windowRect.height(),
        windowImageRect.width(), windowImageRect.height()), pixmap, windowImageRect);

    window->close();
}

return resultImg;

最终效果

Post Views: 16

相关

Warning: Missing argument 1 for cwppos_show_review(), called in /home/wwwroot/www.mycode.net.cn/wp-content/themes/flat/content-single.php on line 29 and defined in /home/wwwroot/www.mycode.net.cn/wp-content/plugins/wp-product-review/includes/legacy.php on line 18

Notice: Undefined variable: post_id in /home/wwwroot/www.mycode.net.cn/wp-content/plugins/wp-product-review/includes/legacy.php on line 20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏水击三千

android 补间动画

android开发过程中,为了更好的展示应用程序,应用程序添加动画,能够很好地实现这个功能。如果动画中的图像变化有一定的规律,可以采用自动生成图像的方式来生成动...

26110
来自专栏三木的博客

CSS概要

CSS 基础知识 语法 CSS全称为“层叠样式表 (Cascading Style Sheets)”,它主要是用于定义HTML内容在浏览器内的显示样式, 如文字...

1975
来自专栏编程

【CSS】格仔背景

CSS代码 * { margin: 0; padding: 0; } html, body { width: 100%; height: 100%; } bod...

24710
来自专栏cnblogs

深入理解及应用Position

position俗称定位,主要取值及作用如下: static 默认值。没有定位,出现在正常文档流中 absolute 绝对定位,相...

2046
来自专栏腾讯IVWEB团队的专栏

Canvas 实现 progress 效果

分享下一个简单的Canvas插件 ,Canvas元素是HTML5的一部分,允许脚本语言动态渲染位图像,你可以使用javascript用它来绘制图形。

5580
来自专栏九彩拼盘的叨叨叨

移动端页面使用rem来做适配

rem(font size of the root element)是指相对于根元素(即html元素)的字体大小的单位。 假设根元素的字体大小是10px, 则...

1103
来自专栏Android小菜鸡

自定义View学习之路(五)————彩虹环

  走往android的进阶之路,避不开自定义View的学习和绘制。这里以绘制一个可用的彩虹环为例。开始系统的学习View的绘制。 彩虹环需求:

982
来自专栏静晴轩

CSS征途之Background点滴

CSS虽算不上编程语言,确是能够真正做到网页表现与内容分离的一种样式设计语言。相对于传统HTML的表现而言,CSS能够对网页中的对象的位置排版进行像素级的精确控...

3354
来自专栏抠抠空间

css属性详解

一、字体属性 字体   font-family可以把多个字体名称作为一个“回退”系统来保存。如果浏览器不支持第一个字体,则会尝试下一个。浏览器会使用它可识...

3019
来自专栏JavaEdge

CSS 全解析实战(三)-CSS 基础1 选择器(1)背景非布局样式(边框)非布局样式(滚动)非布局样式(文本折行)非布局样式(装饰性属性)hack和案例(1)hack和案例(2)面试题

571

扫码关注云+社区

领取腾讯云代金券