Qt 2D繪圖(7):QImage類(操縱像素、掃描線、圖像轉換)

Qt 2D繪圖(7):QImage類(操縱像素、掃描線、圖像轉換)

若對C++語法不熟悉,建議參閱《C++語法詳解》一書,電子工業出版社出版,該書語法示例短小精悍,對查閱C++知識點相當方便,並對語法原理進行了透徹、深入詳細的講解,可確保讀者徹底弄懂C++的原理,徹底解惑C++,使其知其然更知其所以然。此書是一本全面瞭解C++不可多得的案頭必備圖書。

QImage的基本函數的使用與QPixmap類似,對這些函數的講解本文從略。

1、QImage圖像的存儲
存儲爲QImage的圖像,每個像素使用一個整數表示,QImage默認支持的文件格式與QPixmap相同。
單色圖像的存儲:具有1位深度的圖像(即單色圖像)被存儲到最多擁有兩種顏色的顏色表中。單色圖像有兩種類型:大端(MSB)或小端(LSB)位順序。
8位深度圖像的存儲:使用一個8位的索引把8位圖像存儲到顏色表中,因此8位圖像的每個像素佔據一字節(8位)的存儲空間,即每個像素的顏色與顏色表中某個索引號的顏色相對應。
顏色表使用QVector存儲,QRgb類型是使用typedef定義的一個unsigned int類型,該類型包含一個0xAARRGGBB格式的四元組ARGB格式。
32位圖像沒有顏色表,每個像素直接包含一個QRgb類型的值,共有3種類型的32位圖像,分別是RGB(即0xffRRGGBB),ARGB和預乘的ARGB,在預乘格式中,紅、綠、藍通道需乘以alpha分量除以255(詳見下一點對alpha的講解)。
2、對alpha通道的處理(預乘alpha)
帶alpha通道的圖像有兩種處理alpha通道的方法,一種是直接alpha,別一種是預乘alpha,使用預乘alpha通道的圖像通常會更快。
直接alpha圖像的RGB數值是原始的數值,而預乘alpha圖像的RGB數值是乘以alpha通道後得到的數值,比如ARGB = (a , r , g, b);則預乘alpha後的值爲(a, a * r, a * g, a * b);

Qt 預乘alpha通道圖像的算法是把紅、綠、藍通道的數值乘以alpha通道的數值再除以255,比如使用ARGB32格式表示的0x7F00004E,使用預乘ARGB32格式應表示爲0x7F000027,算法爲(0x4E * 0x7F) / 0xFF ≈ 0x27,把(0x4E * 0x7F) / 0xFF 變換一下爲0x4E * ( 0x7F / 0xFF ) 其中 0x7F / 0xFF是以小數表示的alpha值。再如對於透明度爲80% (1-33/FF)的ARGB32格式表示的 0x3337304B,使用預乘ARGB32格式應爲0x330B170F,以0x37 爲例計算0x37 * (0x33/0xFF),轉換爲10進制後爲55 * 0.2 = 11=0xB。
3、表12-26爲QImage類需要使用到的描述圖像格式的枚舉QImage::Format(完整的枚舉可參閱幫助文檔)
在這裏插入圖片描述

8、顏色表和操控像素
QRgb類型與qRgb()函數:QRgb類型相當於是使用typedef重命名的unsigned int類型,該類型用於保存顏色,其格式爲#AARRGGBB的ARGB四元組。qRgb()是一個用於設置QRgb類型顏色的全局函數,比如qRgb(111,1,1)表示返回一個紅色的QRgb類型的顏色。

23)、void setColorCount(int colorCount); 	//設置顏色表的大小。
int colorCount() const;				//返回顏色表的大小,注意:對於32位深度的圖像該函數返回0
24)、void setColorTable(const QVector<QRgb> colors);    //設置顏色表。
QVector<QRgb> colorTable() const;		//返回顏色表中包含的顏色列表,若沒有顏色表,則返回空列表
25)、QRgb color(int i) const;
	返回位於索引i(從0計數)處的顏色表中的顏色,顏像表中的顏色使用ARGB四元組(QRgb)表示,可使用::qAlpha(QRgb),::qRed(QRgb),::qGreen(QRgb),::qBlue(QRgb)獲取這些元素。
26)、void setColor(int index, QRgb colorValue);
	把顏色表中索引爲index處的顏色設置爲colorValue,顏色具有ARGB四元組,若index超出了顏色表的當前大小,則使用setColorCount()進行擴展。
27)、QRgb pixel(int x, int y) const;       	
QRgb pixel(const QPoint &position) const;
	返回位置position處像素的顏色,若該位置無效,則結果是未定義的。當用於大規模像素操作時,此函數很昂貴,若需要讀取多個像素,則使用constBits()或constScanLine()函數。
28)、void setPixel(const QPoint &position, uint index_or_rgb);
void setPixel(int x, int y, uint index_or_rgb);
	把位置position或座標(x,y)處像素的索引或顏色設置爲index_or_rgb。若圖像的格式是單色或調色板的,則index_or_rgb必須是顏色表的索引,否則必須是QRgb值。
	該函數很昂貴,若需要考慮性能,建議使用scanLine()或bits()直接訪問像素數據。
29)、QColor pixelColor(const QPoint &position) const;    	//qt5.6
QColor pixelColor(int x, int y) const;   				//qt5.6
	以QColor的形式返回位置position或座標(x,y)處像素的顏色。
30)、void setPixelColor(const QPoint &position, const QColor &color);   	//qt.56
void setPixelColor(int x, int y, const QColor &color);			//qt.56
	把位置position或座標(x,y)處像素的顏色設置爲color
31)、int pixelIndex(const QPoint &position) const;
int pixelIndex(int x, int y) const;
	返回位置position或座標(x,y)處的像素索引,若position無效,或圖像不是調色板(顏色表)圖像(即位深大於8的圖像),則結果是未定義的。

示例12.36:顏色表及像素操控(結果見圖12-76)

//m.h文件的內容
#ifndef M_H
#define M_H
#include<QtWidgets>
class B:public QWidget{    Q_OBJECT
public:	B(QWidget *p1=0):QWidget(p1){}
void paintEvent(QPaintEvent *e){
   		QPainter pr;
  		QImage pi(200,200,QImage::Format_Mono);	//創建一個單色圖像)
    		QRgb r1=qRgb(111,1,1);  	//紅色,注意:顏色是使用qRgb()創建的
    		QRgb r2=qRgb(1,111,1);  	//綠色
    		QRgb r3=qRgb(111,111,1);	//黃色
    //繪製單色圖未填充時的默認樣式
    		pr.begin(this);    pr.drawImage(11,11,pi);    pr.end();
    		qDebug()<<pi.colorTable();	//輸出默認顏色表QVector(4278190080, 4294967295),其中
//4278190080=0xFF00 0000,4294967295=0xFFFF FFFF
    		pi.fill(Qt::color1);   		//使用單色圖的顏色表中的索引爲1的顏色(綠色)填充圖像pi。
 //以下步驟用於設置顏色表
    		QVector<QRgb> v;   v.append(r1);    v.append(r2);
    		pi.setColorCount(2);   		//設置顏色表中的顏色數量
    		pi.setColorTable(v);   		//設置一個新顏色表(包含紅色和綠色)
//使用單色圖的新顏色表逐像素繪製一個矩形(22,22,128,100)
    		pr.begin(&pi);
    		for(int j=22;j<122;j++)
    			for(int i=22;i<150;i++){
    			pi.setPixel(i,j,0);}	//使用新顏色表中索引爲0的顏色(紅色)填充位於(i,j)處的像素。
    		pr.end();

    		pr.begin(this);
    		pr.drawImage(222,11,pi);  	//在QWidget中繪製pi
    		qDebug()<<pi.colorTable();	//輸出新設置的顏色表QVector(4285464833, 4278284033),
//其中4285464833=0xFF6F 0101,4278284033=0xFF01 6F01,0x6F=111
    		pi.setColor(1,r3); //把新顏色表中索引爲1的顏色更改爲r3(黃色),注意:setColor()函數
//改變的是顏色表中的顏色,setPixel()才能改變圖像中某個位置的像素顏色。
    		pr.drawImage(444,11,pi);  	//在QWidget重新繪製pi
    		qDebug()<<pi.colorTable();	//輸出更改後的顏色表QVector(4285464833, 4285492993)
                              //其中4285492993=0xFF6F 6F01,0x6F=111
 		pr.end();	}};
#endif // M_H

//m.cpp文件內容
#include "m.h"
int main(int argc, char *argv[]){    QApplication app(argc,argv);
   B w;    w.resize(444,333);    w.show();    return app.exec();  }

在這裏插入圖片描述

9、掃描線
掃描線是指把圖像按水平方向分割成很多條線,每條線就是一條掃描線,說簡單點,一條掃描線就是圖像中的一行,比如對於300*200的圖像,共有200條掃描線(即,有200行)。
注:以下各函數的區別爲,返回類型包含const的函數未使用隱式共享,未包含const的函數使用了隱式共享。

32)、uchar *bits();
const uchar *bits() const;			//返回指向第一個像素數據的指針,相當於scanLine(0)。
33)、const uchar *constBits() const;	//返回指向第一個像素數據的指針。
34)、uchar *scanLine(int i);
const uchar *scanLine(int i) const;
	返回指向具有索引爲i(從0計數)的掃描線上的像素數據的指針,掃描線數據是在32位邊界上對齊的。若要訪問32位深度的圖像數據,可把返回的指針強制轉換爲QRbg*格式,然後再進行讀/寫像素值。
35)、const uchar *constScanLine(int i) const;
	返回指向掃描線上具有索引爲i(從0計數)的像素數據的指針,掃描線數據是在32位邊界上對齊的。
36)、int bytesPerLine() const;
	返回每個圖像掃描線的字節數,若height()不爲零,則相當於sizeInBytes()/height();

示例12.37:掃描線的使用(結果見圖12-77)

void paintEvent(QPaintEvent *e){
    QPainter pr;
    QImage pi(200,200,QImage::Format_ARGB32);
    QImage pi1(200,200,QImage::Format_ARGB32);
    pi.fill(qRgb(1,111,1));     pi1.fill(qRgb(1,111,1));   //使用綠色填充背景
//使用掃描線函數返回的指針逐像素繪製一個矩形
//獲取指向第一行第一個像素的指針,並將其強制轉換爲QRgb*以方便修改。
    	QRgb *pu=(QRgb*)pi.bits();
for(int i=0;i<20000;i++)     //逐像素設置每個像素的顏色,pi每行有200個像素,
//循環2萬次意味着設置100行像素的顏色。
        		{   *pu=qRgb(111,1,1);pu++; }
    	QRgb *pu1=(QRgb*)pi1.scanLine(19);  //獲取第20行掃描線第一個元素的指針
    		for(int i=0;i<20000;i++){    *pu1=qRgb(111,1,1);pu1++;  }
    pr.begin(this);
    pr.drawImage(11,11,pi);   pr.drawImage(222,11,pi1);
qDebug()<<pi1.bytesPerLine();  //輸出800(字節),因爲pi是32位的ARGB圖像,
//每個像素佔據4字節,每一行有200個像素,
//因此每個掃描線佔據800個字節大小。
    pr.end();}

在這裏插入圖片描述

10、圖像格式或類型轉換

37)、QImage convertToFormat(Format format, Qt::ImageConversionFlags flags = Qt::AutoColor) const;
QImage convertToFormat(Format format, const QVector<QRgb> &colorTable, Qt::ImageConversionFlags flags = Qt::AutoColor) const;
	把該圖像轉換爲使用指定的顏色表colorTable和格式format的圖像。從RGB格式到索引格式的轉換是一種緩慢的操作,並且將使用最近的顏色,而且還沒有抖動。枚舉Qt::ImageConversionFlag見表12-21
38)、void invertPixels(InvertMode mode = InvertRgb);
	返轉圖像中的所有像素值,參數mode僅在圖像深度爲32位時纔有意義,默認爲InverRgb。若圖像具有顏色表(8位或1位深度圖像),則把顏色替換爲255減去當前顏色索引的像素。若圖像是具有預乘alpha通道的,則首先把圖像轉換爲ARGB32,然後進行反轉,最後再將其轉換回來。InvertMode枚舉見表12-27

在這裏插入圖片描述

39)、QImage rgbSwapped() const;
	返回一個QImage,新圖像交換了所有像素的紅色和藍色分量的值,從而把RGB圖像轉換爲BGR圖像。原始QImage不會被改變。
40)、bool reinterpretAsFormat(Format format);   					//qt5.9
	在不更改數據的情形下更改圖像的格式,僅適用於具有相同深度的格式,若成功,則返回true。注意:該函數不會檢查圖像數據在新格式中是否有效,若深度兼容,則仍會返回true。
41)、CGImageRe toCGImage() const;    						//qt5.10
	創建與此QImage等效的CGImage,並返回CGImageRef句柄。
42)、void swap(QImage &other);   							//把此圖像與other交換。
43)、static QImage::Format toImageFormat(QPixelFormat format);     	//靜態的
	把格式format轉換爲QImage::Format。
44)、static QPixelFormat toPixelFormat(QImage::Format format);      	//靜態的
	把格式format轉換爲QPixelFormat。

示例12.38:圖像轉換(結果見圖12-78)

void paintEvent(QPaintEvent *e){
    QPainter pr(this);
    QImage pi2("F:/1z.png");
    qDebug()<<pi2.format();   								//輸出5(即ARGB32格式)
    pr.drawImage(11,11,pi2);   								//繪製原始圖像
    QImage pi3= pi2.convertToFormat(QImage::Format_Grayscale8); 	//轉換爲灰度圖
    pr.drawImage(11,222,pi3);
    QImage pi4= pi2.rgbSwapped();   							//交換紅色和藍色通道
    pr.drawImage(222,222,pi4);
pi2.invertPixels();    //反轉所有像素的顏色,注意,該函數會破壞pi2(但不會破壞原始文件1z.png)
    pr.drawImage(222,11,pi2);
    pr.end();		}

在這裏插入圖片描述

本文作者:黃邦勇帥(原名:黃勇)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章