本篇读书笔记主要记录QPainter的复合模式&&双缓冲绘图&&绘图中的其他问题
 
大纲:
    复合模式
    双缓冲绘图
    绘图中的其他问题
        重绘事件
        剪切
        读入和写入图像
        播放GIF
        渲染SVG
 
复合模式
QPainter提供了复合模式(Composition Modes)来定义如何完成数字图像的复合,即如何将源图像的像素和目标图像的像素进行合并。QPainter 提供的常用复合模式及其效果如下面截图所示,所有的复合模式可以在QPainter的帮助文档中进行查看。
 
最普通的类型是SourceOver(不设置的话默认是这个)(通常被称为alpha混合),就是正在绘制的源像素混合在已经绘制的目标像素上,源像素的alpha分量定义了它的透明度,这样源图像就会以透明效果在目标图像上进行显示。
 
若绘图设备是QImage,图像的格式一定要指定为QImage::Format_ARGB32_Premultiplied或者Format_ARGB32,不然复合模式就不会产生任何效果。
当设置了复合模式,它就会应用到所有的绘图操作中,如画笔、画刷、渐变和pixmap/image绘制等。
 
演示书上的代码:
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter; QImage image(,,QImage::Format_ARGB32_Premultiplied);
// 使用绘图设备,绘制到绘图设备上
painter.begin(&image);
// 绘制一个矩形
painter.setBrush(Qt::green);
painter.drawRect(,,,); //在四个角分别绘制一个矩形,使用不同的复合模式(composition)
painter.setBrush(QColor(,,,));
//composition没有设置则使用默认的SourceOver
painter.drawRect(,,,);
//QPainter::CompositionMode_SourceIn
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
painter.drawRect(,,,);
//QPainter::CompositionMode_DestinationOver
painter.setCompositionMode(QPainter::CompositionMode_DestinationOver);
painter.drawRect(,,,);
//QPainter::CompositionMode_Xor
painter.setCompositionMode(QPainter::CompositionMode_Xor);
painter.drawRect(,,,);
painter.end(); // 绘制到当前部件(当前绘图设备是QWidget的子类,也就是部件)
painter.begin(this);
painter.drawImage(,,image);
}
效果图:
 
 
双缓冲绘图
所谓双缓冲(double-buffers)绘图,就是在进行绘制时,先将所有内容都绘制到一个绘图设备(如QPixmap)上,然后再将整个图像绘制到部件上显示出来。
使用双缓冲绘图可以避免显示时的闪烁现象。
从Qt4.0开始,QWidget部件的所有绘制都自动使用了双缓冲,所以一般没有必要在paintEvent()函数中使用双缓冲代码来避免闪烁。
虽然在一般的绘图中无须手动使用双缓冲绘图,不过要想实现一些绘图效果,还是要借助于双缓冲的概念。
 
下面的程序实现使用鼠标在界面上绘制一个任意大小的矩形的功能。
这里需要两张画布,它们都是QPixmap实例。
其中一个tempPix用来作为临时缓冲区,当鼠标正在拖动矩形进行绘制时,将内容先绘制到tempPix上,然后将tempPix绘制到界面上;
而另一个pix作为缓冲区,用来保存已经完成的绘制。当松开鼠标完成矩形的绘制后,则将tempPix的内容复制到pix上。
为了绘制时不显示拖影,在移动鼠标过程中,每绘制一次都要在刚开始绘制这个矩形的图像上进行绘制,所以需要在每次绘制tempPix之前,先将pix的内容复制到tempPix上。
#include "widget.h"
#include "ui_widget.h" #include <QMouseEvent>
#include <QPainter> Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
pix_buffer_ = QPixmap(, );
pix_buffer_.fill(Qt::white);
temp_pix_buffer_ = pix_buffer_;
is_drawing_ = false;
} Widget::~Widget() { delete ui; } void Widget::mousePressEvent(QMouseEvent *event) {
if(event->button() == Qt::LeftButton){
//当鼠标左键按下时获取当前位置作为矩形的开始点
start_point_ = event->pos();
is_drawing_ = true;
}
} void Widget::mouseMoveEvent(QMouseEvent *event) {
if(event->button() & Qt::LeftButton){
//当按着鼠标左键进行移动时,获取当前位置作为结束点,绘制矩形
end_point_ = event->pos();
//将缓冲区的内容复制到临时缓冲区,这样进行动态绘制时
//每次都是在缓冲区图像的基上进行绘制,就不会产生拖影现象了
temp_pix_buffer_ = pix_buffer_;
// 更新显示
update();
}
} void Widget::mouseReleaseEvent(QMouseEvent *event) {
if(event->button()== Qt::LeftButton){
//当鼠标左键松开时,获取当前位置为结束点,完成矩形绘制
end_point_ = event->pos();
//标记已经结束绘图
is_drawing_ = false;
update();
}
} void Widget::paintEvent(QPaintEvent *) {
int x = start_point_.x();
int y = start_point_.y();
int width = end_point_.x();
int height = end_point_.y(); QPainter painter;
painter.setPen(QColor(Qt::red));
painter.begin(&temp_pix_buffer_);
painter.drawRect(x,y,width,height);
painter.end(); painter.begin(this);
painter.drawPixmap(,,temp_pix_buffer_);
//如果已经完成了绘制,那么更新缓冲区
if(!is_drawing_){
pix_buffer_ = temp_pix_buffer_;
}
}
这里先在临时缓冲区中进行绘图,然后将其绘制到界面上。最后判断是否已经完成了绘制,如果是,则将临时缓冲区中的内容复制到缓冲区中,这样就完成了整个矩形的绘制。这个例子中的关键是pix和tempPix的相互复制,如果想将这个程序进行扩展,可以查看一下网站上的涂鸦板序。
 
与这个例子很相似的一个应用是橡皮筋线,就是我们在Windows桌面上拖动鼠标出现的橡皮筋选择框。Qt中提供了QRubberBand 类来实现橡皮筋线,使用它只需要在几个鼠标事件处理函数中进行设置即可,具体应用可以查看该类的帮助文档。
 
绘图中的其他问题
重绘事件 paintEvent(QPaintEvent *event)override; protected
前面讲到的所有绘制操作都是在重绘事件处理函数paintEvent()中完成的,它是QWidget类中定义的函数。也就意味着QtWidgets都是绘图设备
一个重绘事件用来重绘一个部件的全部或者部分区域,下面几个原因中的任意一个都会发生重绘事件:
  • repaint()函数或者update()函数被调用;
  • 被隐藏的部件现在被重新显示;
  • other
大部分部件可以简单地重绘它们的全部界面,但是一些绘制比较慢的部件需要进行优化而只绘制需要的区域(可以使用QPaintEvent::region()来获取该区域),这种速度上的优化不会影响结果。
Qt也会通过合并多个重绘事件为一个事件来加快绘制,当update()函数被调用多次,或者窗口系统发送了多个重绘事件时,那么Qt就会合并这些事件成为一个事件,而这个事件拥有最大的需要重绘的区域。
update()函数不会立即进行重绘,要等到Qt返回主事件循环后才会进行,所以多次调用update()函数一般只会引起一次paintEvent()函数调用。
 
但是调用repaint()函数会立即调用paintEvent()函数来重绘部件,只有在必须立即进行重绘操作的情况下(比如在动画中),才使用repaint()函数。
update()允许Qt优化速度和减少闪烁,但是repaint()函数不支持这样的优化,所以建议一般情况下尽可能使用update()函数。
还要说明一下,在程序开始运行时就会自动发送重绘事件而调用paintEvent()函数。另外,不要在paintEvent()函数中调用update( )或者repaint()函数。
 
当重绘事件发生时,要更新的区域一般会被擦除,然后在部件的背景上进行绘制。
部件的背景一般可以使用setBackgroundRole( )来指定,然后使用setAutoFillBackground(true)来启用指定的颜色。
例如,使界面显示比较深的颜色,则可以在部件的构造函数中添加如下代码:
 
setBackgroundRole(QPalette::Dark);
setAutoFillBackgroud(true);
 
剪切
QPainter可以剪切任何的绘制操作,它可以剪切一个矩形、一个区域或者一个路径中的内容,
这分别可以使用setClipRect()、setClipRegion()和setClipPath()函数来实现。
剪切会在QPainter的逻辑坐标系统中进行。下面的代码实现了剪切一个矩形中的文字:
 
QPainter painter(this);
// 剪切矩形中的内容
painter.setClipRect(,,,);
painter.drawText(,,"tudouTestTestTest");
 
读入和写入图像
要读取图像,最普通的方法是使用QImage或者QPixmap的构造函数,或者调用Qlmage: :load()和QPixmap::load()函数。
 
Qt中还有一个QImageReader类,该类提供了一个格式无关的接口,可以从文件或者其他设备中读取图像。
QImageReader 类可以在读取图像时提供更多的控制,例如,可以使用setScaledSize()函数将图像以指定的大小进行读取,还可以使用setClipRect()读取图像的一个区域。
由于依赖于图像格式底层的支持,QImageReader的这些操作可以节省内存和加快图像的读取
另外,Qt还提供了QImageWriter类来存储图像,它支持设置图像格式的特定选项,比如伽玛等级、压缩等级和品质等。
当然,如果不需要设置这些选项,那么可以直接使用Qlmage: :save()和QPixmap::save( )函数。
 
播放GIF
QMovie类是使用QImageReader来播放动画的便捷类,使用它可以播放不带声音的简单的动画,比如gif文件格式。
这个类提供了很方便的函数来进行动画的开始、暂停和停止等操作。
 
渲染SVG
可缩放矢量图形( Scalable Vector Graphics,SVG)是一个使用XML来描述二维图形和图形应用程序的语言。
在Qt中可以使用QSvgWidget类加载一个SVG文件,而使用QSvgRenderer类在QSvgWidget中进行SVG文件的渲染。
这两个类的使用很简单,可以参考SVG Generator Example和SVG Viewer Example示例程序。
 
Qt参考示例和文档
文档:
示例:Image Composition Example , Composition Modes
 
本篇源代码
https://github.com/tudouloveloli/QtExampleCode

[Qt2D绘图]-06QPainter的复合模式&&双缓冲绘图&&绘图中的其他问题的更多相关文章

  1. VC使用双缓冲避免绘图闪烁的正确使用方法【转】

    使用内存DC绘图,然后实现双缓冲,避免绘图闪烁,这个小技术简单但很有效.但是仍然有很多人说使用了双缓冲,图片却仍然有闪烁,分析了几个这样的例子,发现 其实不是双缓冲的技术问题,而是使用者没有正确理解和 ...

  2. VC使用双缓冲制作绘图控件

    最近用VC做了一个画图的控件.控件在使用的时候遇到点问题.在控件里画了图之后切换到其他页面,等再切换回来的时候,发现控件里画的图都不见了.这是因为VC里面,当缩小.遮挡页面后客户区域就会失效,当再次显 ...

  3. Android开发之用双缓冲技术绘图

    双缓冲技术主要用在画图,动画效果上,其原理就是:将资源先载入到缓冲区,然后再将缓冲区整个载入到View上面去. 双缓冲技术可以有效防止闪烁,提高显示质量. DrawView.java: package ...

  4. VC GDI双缓冲机制绘图防屏幕闪烁实现步骤

    在OnDraw(CDC* pDC) 中添加如下代码 CDC MemDC; //首先定义一个显示设备对象 CBitmap MemBitmap;//定义一个位图对象 //随后建立与屏幕显示兼容的内存显示设 ...

  5. Qt组件中的双缓冲无闪烁绘图

      双缓冲绘图在Qt4中,所有的窗口部件默认都使用双缓冲进行绘图.使用双缓冲,可以减轻绘制的闪烁感.在有些情况下,用户要关闭双缓冲,自己管理绘图.下面的语句设置了窗口部件的Qt::WA_PaintOn ...

  6. C#-gdi绘图,双缓冲绘图,Paint事件的触发

    一. 画面闪烁问题与双缓冲技术 1.1 导致画面闪烁的关键原因分析: 1  绘制窗口由于大小位置状态改变进行重绘操作时 绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面 ...

  7. [Android学习笔记]双缓冲绘图技术

    双缓冲技术绘图: 什么情况下产生的双缓冲技术?当数据量很大时,绘图可能需要花费很长的时间,这样屏幕就会出现卡顿,闪烁等现象. 什么是双缓冲技术?双缓冲是在内存中创建一个与屏幕绘制区域一致的对象,先将图 ...

  8. java的双缓冲技术

    Java的强大特性让其在游戏编程和多媒体动画处理方面也毫不逊色.在Java游戏编程和动画编程中最常见的就是对于屏幕闪烁的处理.本文从J2SE的一个再现了屏幕闪烁的Java Appilication简单 ...

  9. C# GDI+双缓冲技术

    我想有很多搞图形方面的朋友都会用到双缓冲技术的时候,而且有的时候她的确是个头疼的问题.最近我也要用双缓冲技术,程序怎么调试都不合适,当要对图形进行移动时,总是会出现闪烁抖动.在网上找了些资料,说得都不 ...

随机推荐

  1. CODING DevOps 系列第一课:基于开源工具链打造持续交付平台

    当下软件发展趋势 当今 IT 行业发展中比较流行的几个技术,首先是微服务化,将原有的一个系统拆分成多个,意味着有多个系统需要构建.测试.部署和运维. 第二个是敏捷开发模式,需求粒度更细化,要求一个可独 ...

  2. upd套接字服务器与客户端交互C++代码示范

    upd套接字服务器与客户端交互C++代码示范 // vc2_2_4UDPserver_Txwtech.cpp : 定义控制台应用程序的入口点. //服务器端 #include "StdAfx ...

  3. 004.OpenShift命令及故障排查

    一 CLI访问OpenShift资源 1.1 资源操作 OCP将OpenShift集群中的为由主节点管理的对象统称为资源,如:node.service.pod.project.deployment.u ...

  4. android 中使用自定义权限在广播中的利用

    1.在一个进程中发送一个有自定义权限的广播,另外一个进程中拥有广播接受者接受到该广播 <?xml version="1.0" encoding="utf-8&quo ...

  5. MongoDB via Dotnet Core数据映射详解

    用好数据映射,MongoDB via Dotnet Core开发变会成一件超级快乐的事.   一.前言 MongoDB这几年已经成为NoSQL的头部数据库. 由于MongoDB free schema ...

  6. JAVA集合框架 - Collection

    collection大致介绍 Collection是集合层次结构中的根接口. 集合表示一组对象.有些集合允许重复元素,有些则不允许.有些是有序的,有些是无序的. JDK没有提供此接口的任何直接实现:它 ...

  7. urllib库使用方法

    这周打算把学过的内容重新总结一下,便于以后翻阅查找资料. urllib库是python的内置库,不需要单独下载.其主要分为四个模块: 1.urllib.request——请求模块 2.urllib.e ...

  8. CListBOX 用法

    ListBox的操作比较简单: 1添加数据 声明控件变量的类别为Control,变量类型为CListBox,变量名为m_ListBox_Content. m_ListBox_Content.AddSt ...

  9. Java中栈和堆讲解

    之前对JVM中堆内存和栈内存都是一直半解,今天有空就好好整理一下,用作学习笔记. 包括Java程序在内,任何程序在运行时都是要开辟内存空间的.JVM运行时在内存中开辟一片内存区域,启动时在自己的内存区 ...

  10. 网络基础和 TCP、IP 协议

    1.网络基本概念 1.1 什么是网络:一些网络设备按照一定的通讯规则(网络协议)进行通讯的系统. 1.2 VPN(虚拟私有网络)加密,相当于专线,从分支机构到总部. 1.3 资源共享的功能和特点: 数 ...