一、抗锯齿渲染

1.1 逻辑绘图

图形基元的大小(宽度和高度)始终与其数学模型相对应,下图示意了忽略其渲染时使用的画笔的宽度的样子。

1.2 物理绘图(默认情况)

在默认的情况下,绘制会产生锯齿,并且使用这样的规则进行绘制: 当使用宽度为一个像素的画笔进行渲染时,像素会在数学定义的点的右边和下边进行渲染,如下图1所示。当使用一个拥有偶数像素的画笔进行渲染时,像素会在数学定义的点的周围对称渲染;而当使用一个拥有奇数像素的面笔进行渲染时,首先按照偶数对称绘制,最后一个像素会被渲染到数学定义的点的右边和下边,如下图2所示。

所以看起来图像不是很平滑,像是有锯齿,所以为了消锯齿,就要用到抗锯齿绘图。

1.3 抗锯齿绘图

抗锯齿( Anti-aliased)又称为反锯齿或者反走样,就是对图像的边缘进行平滑处理,使其看起来更加柔和流畅的一种技术。QPaint er 进行绘制时可以使用QPainter ::RenderHint 渲染提示来指定是否要使用抗锯齿功能,RenderHint 取值分为以下三种。

如果在绘制时使用了抗锯齿渲染提示,即使用 QPainter:: setRenderHint(RenderHint hint, bool on = true) 函数,将参数 hint 设置为了 QPainter:: Antialiasing。那么像素就会在数学定义的点的两侧对称的进行渲染,如下图所示。

示例程序为:

QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); //抗锯齿和使用平滑转换算法

二、坐标系统

2.1 坐标系统简介

Qt的坐标系统是由QPainter类控制的,而QPainter是在绘图设备上绘制的。一个绘图设备的默认坐标系统中原点(0, 0)在其左上角,x坐标向右增长,y坐标向下增长。在基于像素的设备上,默认的单位是一个像素,而在打印机上默认的单位是一个点(1/72英寸)。

下面仍然在上一节的程序中进行代码演示,更改paintEvent()的内容如下:

void Widget::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.setBrush(Qt::red);painter.drawRect(0, 0, 100, 100);painter.setBrush(Qt::yellow);painter.drawRect(-50, -50, 100, 100);
}

我们先在原点(0,0)绘制了一个长宽都是100像素的红色矩形,又在(-50,-50)点绘制了一个同样大小的黄色矩形。可以看到,我们只能看到黄色矩形的四分之一部分。运行程序,效果如下图所示。

2.2 坐标系统变换

默认情况下,QPainter在指定设备的坐标系统上进行绘制,在进行绘图时,使用QPainter::translate()函数平移坐标系统;可以使用QPainter::scale()函数缩放坐标系统;使用QPainter::rotate()函数顺时针旋转坐标系统;还可以使用QPainter::shear()围绕原点来扭曲坐标系统。如下图所示。

我们可以使用前面提到的那些便捷函数进行坐标系统变换,也可以通过QTransform类实现。

(1)平移变换

将paintEvent()函数内容更改如下:

void Widget::paintEvent(QPaintEvent *)
{//平移坐标系统QPainter painter(this);painter.setBrush(Qt::yellow);painter.drawRect(0, 0, 50, 50);//将坐标系原点向右、向下平移100像素点,即使原点坐标变为(100,100)painter.translate(100, 100);painter.setBrush(Qt::red);painter.drawRect(0, 0, 50, 50);//将坐标系原点向左、向上平移100像素点,即重新使原点坐标变为(0,0)painter.translate(-100, -100);painter.drawLine(0, 0, 20, 20);
}

这里先在原点(0, 0)绘制了一个宽、高均为50的正方形,然后使用translate()函数将坐标系统进行了平移,使(100, 100)点成为了新原点,所以我们再次进行绘制的时候,虽然drawRect()中的逻辑坐标还是(0, 0)点,但实际显示出来的却是在(100, 100)点的红色正方形。可以再次使用translate()函数进行反向平移,使原点重新回到窗口左上角。运行程序,效果如下图所示。

(2)缩放变换

将paintEvent()函数中的内容更改如下:

void Widget::paintEvent(QPaintEvent *)
{//缩放坐标系统QPainter painter(this);painter.setBrush(Qt::yellow);painter.drawRect(0, 0, 100, 100);//将坐标系统的横、纵坐标都放大两倍painter.scale(2, 2);painter.setBrush(Qt::red);painter.drawRect(50, 50, 50, 50);
}

可以看到,当我们使用scale()函数将坐标系统的横、纵坐标都放大两倍以后,逻辑上的(50,50)点变成了窗口上的(100, 100)点,而逻辑上的长度50,绘制到窗口上的长度却是100。运行程序,效果如下图所示。

(3)旋转变换

将paintEvent()函数更改如下:

void Widget::paintEvent(QPaintEvent *)
{//旋转坐标系统QPainter painter(this);painter.drawLine(0, 0, 100, 0);//以原点为中心,顺时针旋转30度painter.rotate(30);painter.drawLine(0, 0, 100, 0);painter.translate(100, 100);painter.rotate(30);painter.drawLine(0, 0, 100, 0);
}

这里先绘制了一条水平的直线,然后将坐标系统旋转了30度,又绘制了一条直线。可以看到,默认是以原点(0, 0)为中心旋转的。如果想改变旋转中心,可以使用translate()函数,比如这里将中心移动到了(100, 100)点,然后旋转了30度,又绘制了一条直线。运行程序,效果如下图所示。

(4)扭曲变换

将paintEvent()函数更改如下:

void Widget::paintEvent(QPaintEvent *)
{//扭曲坐标系统QPainter painter(this);painter.setBrush(Qt::yellow);painter.drawRect(0, 0, 50, 50);//纵向扭曲变形painter.shear(0, 1);painter.setBrush(Qt::red);painter.drawRect(50, 0, 50, 50);
}

shear()有两个参数,第一个是对横向进行扭曲,第二个是对纵向进行扭曲,而取值就是扭曲的程度。比如程序中对纵向扭曲值为1,那么就是红色正方形左边的边下移一个单位,右边的边下移两个单位,值为1就表明右边的边比左边的边多下移一个单位。大家可以更改取值,测试效果。运行程序,效果如下图所示。

2.3 ”窗口-视口”转换

在使用QPainter进行绘制时,会使用逻辑坐标进行绘制,然后再转换为绘图设备的物理坐标。逻辑坐标到物理坐标的映射由QPainter的worldTransform()函数和QPainter的viewport()以及window()函数进行处理。其中视口(viewport)表示物理坐标下指定的一个任意矩形,而窗口(window,与以前讲的窗口部件的概念不同)表示逻辑坐标下的相同的矩形。默认的,逻辑坐标和物理坐标是重合的,它们都相当于绘图设备上的矩形。

使用”窗口-视口”转换可以使逻辑坐标系统适合应用的要求,这个机制也可以用来让绘图代码独立于绘图设备。 下面来看一个例子。

创建一个Widget窗口应用(其默认宽400像素,高300像素,左上角为原点)。首先正常绘制一个正方形:

void Widget::paintEvent(QPaintEvent *)
{QPainter p(this);p.setPen(Qt::blue);p.drawRect(0, 0, 100, 100);
}

这个是没有问题的,因为现在的绘图设备就是Widget,其左上角就是原点(0, 0)点。效果如下图所示。

现在我们使用setWindow来设置逻辑坐标矩形:

void Widget::paintEvent(QPaintEvent *)
{QPainter p(this);p.setPen(Qt::blue);p.drawRect(0, 0, 100, 100);p.setWindow(-50, -50, 100, 100);p.setPen(Qt::red);p.drawRect(0, 0, 100, 100);
}

这时,效果如下图所示。

现在来说p.setWindow(-50, -50, 100, 100)的作用,它将逻辑坐标矩形(后面提到的术语window窗口)与我们现在的设备物理坐标矩形(后面提到的术语viewport视口)进行了线性映射,这里所说的设备物理坐标矩形就是我们可见的Widget的坐标,就是左上角为(0, 0)点,宽400,高300这样的矩形,线性映射的示意图如下:

也就是说,调用p.setWindow(-50, -50, 100, 100)之后,再次使用p进行绘制,那么坐标原点就不再是Widget的左上角了,而是到了其中心,以前绘制的宽100、高100的正方形,现在也会按比例变为宽400, 高300,也就是我们看到的这个红色矩形。

再来修改代码:

void Widget::paintEvent(QPaintEvent *){QPainter p(this);p.setPen(Qt::blue);p.drawRect(0, 0, 100, 100);p.setWindow(-50, -50, 100, 100);p.setPen(Qt::red);p.drawRect(0, 0, 20, 20);
}

运行效果如下图所示:

我们将绘制的红色矩形变小,可以明显看到,本应该是个正方形,现在却变成了长方形。就是因为上面说的比例变换造成的,那么怎么才能让它显示应有的形状呢,我们来设置视口:

void Widget::paintEvent(QPaintEvent *)
{QPainter p(this);p.setPen(Qt::blue);p.drawRect(0, 0, 100, 100);int side = qMin(width(), height());p.setViewport((width() - side)/2, (height() - side) /2, side, side);p.setWindow(-50, -50, 100, 100);p.setPen(Qt::red);p.drawRect(0, 0, 20, 20);
}

现在使用setViewport设置视口为一个正方形,就是Widget可是区域上最大的正方形,这样逻辑坐标和物理坐标进行比例变换的时候,红色矩形的宽和高就不会因为缩放的比例不同而发生变形了,如下图所示。

问题提出:那么为什么要修改这个逻辑坐标矩形?

这是为了便于我们绘图,因为我们一般绘图时只是想在标准的坐标系中应该绘制成什么样子,不会考虑不同绘图设备的具体坐标系(比如有的设备坐标原点在其左上角,有的在中心等等),也不会考虑窗口的大小不同而使用不同的代码(比如我们只想在一个宽100、高100的绘图区域的中心绘制一个高20、宽20的正方形,到底实际绘图设备的单位是像素、还是英寸、还是厘米,我们不用考虑)。

引申术语:窗口、视口

这里说的我们想象中的宽100、高100、原点在中心的绘图区域,就是逻辑坐标下的矩形,也就是使用setWindow设置的所谓的窗口;而实际的绘图设备,比如这里的Widget部件,其可视化的区域上设置的一个矩形被称为视口(英文为viewport),默认就是可视化区域的大小,但是可以通过setViewport来设置。窗口与视口相对应,可以进行线性变换,这样,我们就可以通过先设置视口,再设置对应的窗口的方法,来确保我们的代码在标准的想象中的坐标系中绘制的图形,可以准确地显示在不同的绘图设备界面上。

参考:

67 2D绘图(反走样绘图 / 抗锯齿渲染)

Qt 2D绘图部分窗口、视口的研究

转载于:https://www.cnblogs.com/linuxAndMcu/p/11058857.html

Qt 2D绘图之二:抗锯齿渲染和坐标系统相关推荐

  1. 【Qt】2D绘图之抗锯齿渲染

    00. 目录 文章目录 00. 目录 01. 概述 02. 开发环境 03. 逻辑绘图 04. 物理绘图 05. 抗锯齿绘图 06. 附录 01. 概述 本部分的内容可以在帮助文档中通过Coordin ...

  2. [Qt教程] 第12篇 2D绘图(二)渐变填充

    [Qt教程] 第12篇 2D绘图(二)渐变填充 楼主  发表于 2013-4-23 17:59:31 | 查看: 689| 回复: 1 渐变填充 版权声明 该文章原创于Qter开源社区,作者yafei ...

  3. Qt 2D绘图功能简单总结

    文章目录 Qt 2D绘图功能简单总结 Qt 2D绘图功能简单总结 Qt 的绘图功能非常强大,它可以绘制一切想要的图形,从最简单的一条直线到其他任何复杂的图形,还可以用来绘制文本和图片. Qt的绘图系统 ...

  4. ue 抗锯齿 渲染序列失灵_最大的锯齿形序列

    ue 抗锯齿 渲染序列失灵 Problem statement: 问题陈述: Given a square matrix of size n x n, find the sum of the Zigz ...

  5. Qt 2D绘图(5):绘制图像基础

    Qt 2D绘图(5):绘制图像基础 本文为原创文章,转载请注明出处,或注明转载自"黄邦勇帅(原名:黄勇) 本文出自本人原创著作<Qt5.10 GUI完全参考手册>网盘地址: ht ...

  6. Qt中国象棋一—— Qt 2D 绘图入门

    最近想用Qt写一个中国象棋的项目,在网上找了几个例子后,发现关于绘图部分基础为0 ,于是根据项目需要学习一下.查了一些网上的资料,在此总结一下:我比较喜欢的方式是用到什么学什么,或者自己想做一个东西, ...

  7. python 抠图 锯齿_Python | 绘图中的抗锯齿

    python 抠图 锯齿 Antialiasing is another important feature of Matplotlib and in this article, we will re ...

  8. QT 2d绘图优化(一)

    最近在用QT做一款白板软件,在开发过程中,发现了有如下几个问题 void drawLine(QPointF endpt) {path.quadTo(path.currentPosition(),(pa ...

  9. 使用Qt进行2D绘图

    文章目录 基本绘制和填充 基本图形的绘制和填充 1. 绘制图形 2. 使用画笔 3. 使用画刷 渐变填充 1. 线性渐变 2. 径向渐变 3. 锥形渐变 坐标系统 抗锯齿渲染 1. 逻辑表示 2. 抗 ...

最新文章

  1. java list主要实现_java容器-list的常用实现及原理
  2. jpa 删除是否成功_JPA / Hibernate删除实体有时不起作用
  3. boost::search_n相关的测试程序
  4. set,存放不重复数据的集合类型
  5. go语言第一个程序-hello world
  6. html 幻灯禁止上下滑动,幻灯片滑动Immersive Slider
  7. 测试多个输入条件的方法
  8. java重排序_Java内存模型FAQ(四)重排序意味着什么?
  9. matlab 中序列求极值的方法,Matlab中求序列的极值
  10. C++纯虚函数与抽象类
  11. php网页302错误,swfupload提示“错误302”的解决方法
  12. POJ 3087 Shuffle'm Up(水题)
  13. FISCO BCOS Solidity 智能合约 接收数组参数
  14. 【BZOJ3294】放棋子(动态规划,容斥,组合数学)
  15. 刷题记录 kuangbin带你飞专题六:最小生成树
  16. pic单片机 c语言开发环境,《PIC单片机开发环境入门》.pdf
  17. bpsk调制matlab,matlab BPSK 调制与解调
  18. RHCSA-Day1 --- Linux介绍及环境搭建
  19. 云医院HIS系统—医院挂号模块
  20. 融云出海洞察,海外社交市场地区篇

热门文章

  1. python学习 01 变量
  2. 热更新--动态加载framework
  3. 当你用钥匙开不开门时
  4. Flash as3 以鼠标点为中心缩放效果
  5. 【转】修改版WinXP集体歇业避免遭遇调查
  6. 图像检测技术的研究现状
  7. ML 05、分类、标注与回归
  8. 工程架构师对3月14号的白×××人节有什么看法??
  9. 【跃迁之路】【488天】程序员高效学习方法论探索系列(实验阶段245-2018.06.08)...
  10. 【ZZ】Linux常用指令