【qt】绘制图像

本文介绍了图像的基础知识,包括DPI和PPI的概念及其在打印机和显示器中的应用,解释了像素和点的区别。接着,详细讲述了设备像素和设备独立像素在移动设备编程中的作用,以及如何通过设备像素比(dpr)确保图形在不同密度屏幕上的显示一致性。此外,还探讨了颜色基础知识如RGB模型、颜色深度和抖动技术。最后,文章提到了Qt库中处理图像的四个类:QImage、QPixmap、QBitmap和QPicture,以及它们在图像处理和显示中的角色和差异。
摘要由CSDN通过智能技术生成

绘制图像

图像基础知识

  1. 分辨率(DPI和PPI)
    1. DPI和PPI:
      1. DPI全程Dots Per Inch(点每英寸)
      2. PPI全程是Pixel Per Inch(像素每英寸),即指的是每一英寸的长度上有多少个点
    2. DPI和PPI越高,显示就越清楚细腻,DPI通常用于描述打印机或扫描仪等的分辨率,而PPI主要用于描述显示器的分辨率,平常使用中经常混用PPI和DPI的概念,但他们还有一些区别,因为像素和点代表的长度有可能并不是一样长的,对于显示器通常使用PPI,比如1024PPI,是指每英寸有1024个像素,当然,也可对显示器使用DPI,比如1024DPI这时表示以英寸有1024个点
  2. 像素(pixel)
    1. 像素不是长度单位,两者不能等同,像素表示的是一个图像的最小单位,比如室外大屏幕一个像素可能1到2厘米,而电脑显示屏一个像素大约是0.2-0.4毫米长度。比如1024*768分辨率(表示长度方向有1024个像素,宽度方向有768个像素)
    2. 显示屏17英寸的显示器(不含边框),其PPI为: [ ( 1024 ∗ 1024 + 768 ∗ 768 ) 1 2 ] 17 = 75.2 P P I \frac{[(1024*1024 +768*768)^\frac{1}{2}]}{17} =75.2PPI 17[(10241024+768768)21]=75.2PPI,其中一个像素的长度大约为 25.4 75.2 = 0.3377 毫米 \frac{25.4}{75.2} =0.3377毫米 75.225.4=0.3377毫米,其中1英寸为25.4毫米,显示屏的尺寸指的是显示器对角线注意,通常是包括显示器的不可显示边框的),所以本例对分辨率使用勾股定理求出斜边像素数,再除以显示器尺寸就是显示器的PPI
  3. 点(磅)
    1. 在计算机中,一点的大小大约是1/72英寸,1磅就是指的1点的大小
  4. 移动设备编程
    1. 设备像素(物理像素)
      1. 就是显示设备的真实像素,这是屏幕的固有属性,不会改变
    2. 设备独立像素DIP(Device Independent Pixels)
      1. 又称为设备无关像素或密度独立性,在移动设备(比如手机)中,图形被显示在不同密度(分辨率)的屏幕时候桑,这时因不同移动设备的密度并不一致,会导致在低密度的屏幕上使图形看起来更大,而在高密度的屏幕上看起来更小
      2. 为了解决这个问题,引入了设备独立像素,或使用设备独立像素为单位绘制图形,则可以保证图形在不同密度的屏幕上其大小看起来差不多一致。
      3. DIP是由系统设置的,通常系统设置有多个DIP值,比如有120 ppi 、160ppi、240ppi等,通常使用160ppi
      4. DIP的本质就是单位换算问题

      示例:若系统设置的DIP为M 像素/英寸(ppi),设备像素为N 像素/英寸,假设我们绘制X像素长度的图形,若使用DIP绘制图形,求显示屏实际显示的像素Y为多少
      所绘制图形的逻辑长度:L = (X像素) / (M像素/英寸) = X /M 英寸;
      以上公式要注意的是X / M结果的单位是英寸
      因此显示屏实际显示的像素为:Y = ( X / M) * N = X* (N / M) = X*dpr 像素代入实际数据进行计算
      假设DIP为160ppi,设备像素为160 ppi,绘制的长度X为100像素,则在显示屏上实际显示的像素长度为:
      100 (160/160) =100像素,其长度与逻辑长度相同。
      若设备像素为320ppi(即分辨率更高),其余不变,则实际显示的像素长度为:100
      (320/160) = 200像素,
      我们还能计算出显示屏上显示的实际物理长度为:
      200像素/ (320像素/英寸) = 0.625 英寸= 0.625 * 25.4 毫米= 15.875 毫米。
      由以上计算可见当设备像素增长或减小时,dpr会随之增加或减小,这样就保证了所绘图形在不同分辨率的设备上显示的大小基本一致

    3. 设备像素比(dpr):就是设备像素与设备独立像素的比值,dpr通常也是由系统设置的
  5. 颜色基础知识
    1. alpha通常用于记录图像的透明度信息,通常使用8位(共有256种级别)表示alpha通道,0表示透明,255表示不透明
    2. RGB颜色模型:
      1. 自然界中的所有颜色都可由红®、绿(G)、蓝(B)三种颜色组成
      2. 计算机中通常把红、绿、蓝三种颜色中的每一种使用八位进行存储,这样每种颜色就有0-255共256种状态,三种颜色就需要使用24位(三字节)进行存储,不同的红绿蓝就可以组成1600多万( 2 24 2^{24} 224)种颜色,若在24位的基础上在增加一个表示图像透明度的alpha通道,那么ARGB共32位就能组成 2 32 2^{32} 232的组合的颜色。
      3. 除了RGB颜色模型外还有CMY(青色、洋红、黄色)、CMYK、HSI(色调、饱和度、亮度)、HSV等颜色模型
    3. 颜色深度(简称深度):
      1. 就是指一个像素使用多少位来表示颜色,比如红绿蓝各使用8位共24位来表示一个像素的颜色,则颜色深度就是24。因此对于24位深度的颜色,存储一个像素的颜色信息就需要3个字节的空间。颜色深度也被称为“位/像素(bpp)”。
    4. 位平面:
      1. 在计算机中设有专门用于存储图像信息的帧缓存存储器,该存储器中的每一位对应于屏幕上的一个点,当一个位上的点被设置为1时,屏幕上对应位置就出现一个亮点,当一个位的值为零时,则出现一个暗点
      2. 当显示器的分辨率为640480时,为显示一个单色图像(深度为1)需要640480位的帧缓存存储器容量,这个容量被称为一个位平面,同理,若需要显示一个8位深度的图像,则需要有6404808位帧缓存的容量,即需要8个位平面,从此处可见位平面通常与颜色深度的值相同,但也有例外。对于更高的分辨率和显示更高深度的图像,则需要更多的帧缓存容量
    5. 通道:
      1. 是用于描述每个图像颜色的信息的,比如对于RGB模式的图像,共有3个通道,即红色通道、绿色通道、蓝色通道,若该图像还包含alpha通道信息,则该图像就有4个通道。对于CMYK模式的图像,有4个通道,分别为青色、洋红、黄色、黑色通道
    6. 灰度:
      1. 是指黑白图像中的颜色深度,其范围通常使用0~255来表示,其中白色为255,黑色为0。对于RGB模式的彩色图像则RGB三种颜色的值相等时就是灰度色彩模式
  6. 图像抖动(dithering)技术:
    1. 该技术主要用于把高深度的图像转换为低深度图像。比如,老式打印机只有黑点和白点,那么如何打印出具有灰度级别的黑白图片呢?抖动技术就是用于解决这个问题的
    2. 抖动的基本原理就是当人们以足够远的距离观察一个很小的区域时,眼睛会对区域内的细节进行一个平均,从而只能看到区域的总体宽度。在一个区域内黑点越多看起来就越黑,黑点越少看起来就越亮,因此通过适当的控制区域内的黑点数量及分布就能使图片呈现出不同的灰度级别,当然,这有一个专门的抖动算法(本文不讨论)。使用抖动可把灰度图像或彩色图像处理成二值图像。在彩色图像与彩色图像之间的转换(比如把24位的图像转换为8位等)也可以使用抖动技术
  7. 图像的格式:
    1. 像素格式:
      1. 是指像素数据存储时所使用的格式,定义了像素在内存中的编码格式
      2. 在实际存储图像时,对于RGB模式或其他颜色模式并不一定会为每种颜色都分配8位存储空间,比如对于32位含有alpha通道的RGB模式,有可能会为红、绿、蓝各分配10位,而为alpha通道分配2位,当然也有为alpha和红、绿、蓝各分配8位的存储方式
    2. 索引格式(查色表、颜色表、调色板、索引像素格式)
      1. 对于一些包含很少几种颜色的图案,可以为每种出现的颜色构造一张表(称为查色表或颜色表,也称为调色板),在存储图像时只需为每种颜色存储查色表的索引即可,这样可以压缩图像的大小,使用这种方法存储图像的文件,称为索引像索格式文件,常见的文件有GIF和PNG。但是颜色种类太多的图像(比如24位的RGB色),则不适合这种方法存储。比如,对于大小为100100具有4位深度的图像,若使用RGB模型来存储,则每个颜色分量(RGB三色中的其中一种色)需要使用8位存储,因此每个像素需要3字节存储空间,存储该图像就需要1001003≈30KB的存储空间,若为16种颜色创建一个颜色表,则每个像素的颜色只需查找颜色表的索引即可,这样每个像索就只需4位(16种状态需要4位)的存储空间了,该图像像素占据的存储空间为1001000.5 ≈ 5KB,颜色表的大小为3KB16 = 48KB(注意:16种颜色中的每种颜色仍需要24位来描述),总大小约为53KB,比之前节约了为47KB的空间。对于具有24位深度的图像使用颜色表的方法存储,不但节约不了空间,反而还会使空间增大

绘制图像基础

  1. Qt提供4个类处理图像:QImageQPixmapQBitmapQPicture,为了对这几个类加以区分,分别称为QImage为图像、QPixmap为像素图、QBitmap为位图、QPicture为图片
  2. Qt各个处理图像类的区别及作用
    1. QImage类提供了一个与硬件无关的图像表示方法,可以直接访问和操控像素,也就是说该类可修改或编辑图像的像素,该类还可以用于进行I/O处理,并对I/O处理操作进行了优化
    2. QPixmap类主要用于在屏幕上显示图像,QPixmap中的像素数据有底层窗口系统进行管理的,该类不能直接访问和操控像素,只能通过QPainter的相应函数或把QPixmap转换为QImage来访问和操控像素。QPixmap可通过标签(QLabel类)或QAbstractButton的子类(icon属性)显示在屏幕上
    3. QBitmapQPixmap的子类,用于处理颜色深度为1的图像,即只能显示黑白两种颜色
    4. QPicture用来记录并重演QPainter命令,QPicture与分辨率无关,在不同设备上显示。该类使用一个与平台无关的格式(.pic格式)把绘图命令序列化到I/O设备,若有可绘制在QWidget部件或QPixmap上的内容都可以保存在QPicture中,该类的主要作用是把一个绘制图备上使用QPainter绘制的所有图形保存在QPicture之中,然后再把这些图形重新绘制在其他绘图设备上
  3. 通常,可以使用QImage类来加载并操作图像数据,然后把QImage对象转换为QPixmap再显示到屏幕上,若不需对图像进行操作,也可直接使用QPixmap来加载并显示图像
  4. QImageQPixmapQBitmapQPicture都是QPaintDevice类的子类(直接或间接),因此他们都是绘制设备,可以直接在其上进行图形绘制

枚举

  1. 描述图像的转换标志
enum ImageConversionFlag {
  ColorMode_Mask          = 0x00000003,
  /*以下标志用于单设图像*/
  AutoColor               = 0x00000000,//若图像深度为1(即1位),且仅含有黑白像素,则像素图变为黑白图像
  ColorOnly               = 0x00000003,//像素图被抖动转换为本机显示屏深度
  MonoOnly                = 0x00000002,//像素图变为单色,若有必要使用抖动算法进行抖动
  // Reserved             = 0x00000001,
  /*******************************************/

  AlphaDither_Mask        = 0x0000000c,
  /*1位alpha模板(mask)的抖动模式首选项*/
  ThresholdAlphaDither    = 0x00000000,//无抖动(默认)
  OrderedAlphaDither      = 0x00000004,//更快,有序的抖
  DiffuseAlphaDither      = 0x00000008,//使用误差扩散的高质量抖动
  /*******************************************/
  NoAlpha                 = 0x0000000c, // Not supported

  Dither_Mask             = 0x00000030,

  /*以下标志为抖动模式首选项*/
  DiffuseDither           = 0x00000000,//使用误差扩散的高质量抖动。默认
  OrderedDither           = 0x00000010,//更快的,有序的抖动
  ThresholdDither         = 0x00000020,//不抖动,使用最接近的颜色
  // ReservedDither       = 0x00000030,
  /*******************************************/

  DitherMode_Mask         = 0x000000c0,
 /*颜色匹配与抖动首选项*/
  AutoDither              = 0x00000000,//仅在向下转换为1或8位索引格式时才抖动。默认
  PreferDither            = 0x00000040,//转换为较小的色彩空间时,始终使用抖动
  AvoidDither             = 0x00000080,//当源图像使用比目标格式的颜色表的大小更多的不同颜色时,则仅对索引格式使用抖动

  NoOpaqueDetection       = 0x00000100,//不检查图像是否包含非透明像素,若已经明确的知道图像是非透明的,则可以使此标志。若图像没有alpha通道,则此标志不起作用
  NoFormatConversion      = 0x00000200//不对图像进行任何格式转换,比如把QImage转换为QPixmap以用于一次性渲染时,会非常有用
  /*******************************************/

};
Q_DECLARE_FLAGS(ImageConversionFlags, ImageConversionFlag)
Q_DECLARE_OPERATORS_FOR_FLAGS(ImageConversionFlags)

使用QPainter类中绘制图像的函数

  1. 表示在位置point或矩形rectangle内绘制图像image。若图像和矩形大小不一致,图像将缩放以适应其大小
    1. void drawImage(const QRectF &rectangle, const QImage &image)
    2. void drawImage(const QRect &rectangle, const QImage &image)
    3. void drawImage(const QPointF &point, const QImage &image)
    4. void drawImage(const QPoint &point, const QImage &image)
  2. 把图像image的一部分图像(由source指定)绘制到绘制设备的位置point或矩形target内。若图像和矩形大小不一致,图像将缩放以适应其大小
    1. void drawImage(const QRectF &target, const QImage &image, const QRectF &source,Qt::ImageConversionFlags flags = Qt::AutoColor)
    2. void drawImage(const QRect &target, const QImage &image, const QRect &source,Qt::ImageConversionFlags flags = Qt::AutoColor)
    3. void drawImage(const QPointF &point, const QImage &image, const QRectF &source,Qt::ImageConversionFlags flags = Qt::AutoColor)
    4. void drawImage(const QPoint &point, const QImage &image, const QRect &source,Qt::ImageConversionFlags flags = Qt::AutoColor)
    5. void drawImage(int x, int y, const QImage &image, int sx = 0, int sy = 0, int sw = -1, int sh = -1,Qt::ImageConversionFlags flags = Qt::AutoColor)
  3. 在位置point重演图片picture。若point = QPoint(0, 0);则以上函数与QPicture::play()完全相同。
    1. void drawPicture(const QPointF &point, const QPicture &picture)
    2. void drawPicture(const QPoint &point, const QPicture &picture)
    3. void drawPicture(int x, int y, const QPicture &picture)
  4. 在位置point或矩形rectangle内绘制像素图pixmap。若图像和矩形大小不一致,图像将缩放以适应其大小
    1. void drawPixmap(const QPointF &point, const QPixmap &pixmap)
    2. void drawPixmap(const QPoint &point, const QPixmap &pixmap)
    3. void drawPixmap(int x, int y, const QPixmap &pixmap)
    4. void drawPixmap(const QRect &rectangle, const QPixmap &pixmap)
    5. void drawPixmap(int x, int y, int width, int height, const QPixmap &pixmap)
  5. 把像素图pixmap的矩形部分(由source指定)绘制到位置point或目标矩形target内。若图像和矩形大小不一致,图像将缩放以适应其大
    1. void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
    2. void drawPixmap(const QRect &target, const QPixmap &pixmap, const QRect &source)
    3. void drawPixmap(int x, int y, int w, int h, const QPixmap &pixmap, int sx, int sy, int sw, int sh)
    4. void drawPixmap(const QPointF &point, const QPixmap &pixmap, const QRectF &source)
    5. void drawPixmap(const QPoint &point, const QPixmap &pixmap, const QRect &source)
    6. void drawPixmap(int x, int y, const QPixmap &pixmap, int sx, int sy, int sw, int sh)
  6. 在矩形rectangle内绘制一个平铺像素图,其原点位于position,原点是指pixmap左上角的位置
    1. void drawTiledPixmap(const QRectF &rectangle, const QPixmap &pixmap,const QPointF &position = QPointF())
    2. void drawTiledPixmap(const QRect &rectangle, const QPixmap &pixmap,const QPoint &position = QPoint())
    3. void drawTiledPixmap(int x, int y,int width, int height,const QPixmap &pixmap, int sx=0,int sy=0)
  7. 把像素图pixmap拆分为fragmentCount个像素图片段fragments,并以不同比例、旋转和透明度等在多个位置绘制这些像素图片段
    1. void drawPixmapFragments(const PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, PixmapFragmentHints hints = PixmapFragmentHints())

示例

图像绘制

#ifndef WIDGET_H
#define WIDGET_H

#include <QtWidgets>
#include <QRectF>
class aDrawImage :public QWidget
{
    Q_OBJECT
private:
    QPushButton *btn;
    QPicture picture;//图片,用于保存并重演QPainter命令
    QPixmap pixmap;//像素图,用于在屏幕上显示
    QImage image;//图像,用于对图形加载并操作图像数据
    QString fileNmae ="C:/Users/lsz/Desktop/mygit/qt-learning/Qt2d绘图/images/2d绘图系统.png";

    void init(){
        btn =new QPushButton("绘制",this);
        btn->move(22,111);

        connect(btn,&QPushButton::clicked,this,&aDrawImage::draw);

    }
protected:
    void paintEvent(QPaintEvent *event) override{
        Q_UNUSED(event)

        QPainter painter;
        painter.begin(this);


        //把图像绘制到QWidget部件上。槽函数只是创建了图像的内容,但图像需要被显示才会可见
        painter.drawPixmap(11,11,pixmap);
        painter.drawImage(133,11,image);
        painter.drawPicture(244,11,picture);


        painter.end();
    }
private slots:
    void draw(){
        QPainter painter;
        QBrush brush(QColor(255,255,1));
        /* QPixmap、QImage、QPicture都是绘制设备,可在其上直接绘制图形,
         * 因为这些绘制设备都不是QWidget部件,因此可以不在paintEvent()函数中绘制。
         */

        QSize picSize(111,111);
        pixmap.load(fileNmae);
        pixmap =pixmap.scaled(picSize);
        painter.begin(&pixmap);//绘图设备为pixmap
        painter.drawLine(0,0,111,111);
        painter.end();

        image = QImage(QSize(111,111),QImage::Format_RGB32);//深度为32
        painter.begin(&image);
        painter.setBrush(brush);
        painter.drawRect(11,11,122,122);
        painter.end();

        painter.begin(&picture);
        painter.setBrush(brush);//调用bgein会重置为初始状态,因此需要重新设置画刷
        painter.drawRect(11,11,99,99);
        painter.drawArc(11,11,111,111,33*16,144*16);
        painter.end();

//        update();
        repaint();

    }
public:
    aDrawImage(QWidget *p =nullptr) :QWidget(p){ init(); }
};

#endif // WIDGET_H

读取文件的一部分到目标矩形与平铺图像

#ifndef WIDGET_H
#define WIDGET_H

#include <QtWidgets>
#include <QRectF>
class aDrawImage :public QWidget
{
    Q_OBJECT
private:
    QPixmap pixmap;//像素图,用于在屏幕上显示
    QString fileNmae ="C:/Users/lsz/Desktop/mygit/qt-learning/Qt2d绘图/images/2d绘图系统.png";

    void init(){

    }
protected:
    void paintEvent(QPaintEvent *event) override{
        Q_UNUSED(event)

        QPainter painter;
        painter.begin(this);

        pixmap.load(fileNmae);
       // pixmap =pixmap.scaled(QSize(111,111));//调整大小

        //把原图像的(55,55,33,33)部分图形绘制到目标矩形
        painter.drawPixmap(QRect(444,11,111,111),pixmap,QRect(55,55,33,33));
        painter.drawPixmap(QRect(577,11,111,111),pixmap,QRect(55,55,33,33));
        painter.drawPixmap(QRect(700,11,111,111),pixmap,QRect(55,55,33,33));
        //图像平铺到矩形
        painter.drawTiledPixmap(QRect(11,11,400,300),pixmap,QPointF(111,111));


        painter.end();
    }

public:
    aDrawImage(QWidget *p =nullptr) :QWidget(p){ init(); }
};

#endif // WIDGET_H
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值