生活随笔
收集整理的這篇文章主要介紹了
【笔记】COA课内实验-MMX指令集
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前言
參考資料
關于BMP文件格式的詳解
SetDIBitsToDevice函數
SetDIBitsToDevice function | Microsoft docs
BITMAPINFO structure
BITMAPINFOHEADER structure
MMX匯編指令優化
MMX指令集系列之三----數據飽和壓縮與重排指令
原理學習
1. BMP-DIB位圖編碼
什么是BMP
BMP(全稱Bitmap,位圖)是Windows中的標準圖像文件格式,其中有一類叫做設備無關位圖(DIB)。
BMP文件存儲數據時,圖像的掃描方式是按從下到上,從左到右的順序。
BMP文件按深度分為多類,如16色,256色,24位,32位,以下均按32位位圖。
BMP文件編碼依次為:位圖文件頭+位圖信息頭+調色板+數據區。(32位位圖沒有調色板)位圖信息編碼
僅列舉需要用到的編碼,單位為字節(8位)
0x02-0x05 表示整個文件的大小。(注意要把每個區域按字節倒過來讀,即0x05,0x04,0x03,0x02,下同)
0x0A-0x0D 表示從文件開始到數據區的偏移量
0x12-0x15 位圖寬度(單位為像素,下同)
0x16-0x19 位圖高度
0x1C-0x1D 像素位數,24位即0x18
0x22-0x25 數據區的大小位圖數據編碼
一般從0x36開始。
32位位圖一個像素正好需要四個字節,從左到右4個字節分別是BGRA,即藍,綠,紅,透明。
32位位圖不需要強制對齊,因為它本身就四字節對齊。
2. 繪圖API
GetDC function
HDC
GetDC(HWND hWnd
);
得到一個DeviceContext句柄對象,可以在上面畫像素,調用時輸入null即可。
SetDIBitsToDevice function
使用來自圖片的顏色數據來設置目標設備矩形區域上的像素
int SetDIBitsToDevice(HDC hdc
, int xDest
, int yDest
, DWORD w
, DWORD h
, int xSrc
, int ySrc
, UINT StartScan
, UINT cLines
, const VOID
*lpvBits
, const BITMAPINFO
*lpbmi
, UINT ColorUse
);
BITMAPINFO 數據結構
內部還有一個BITMAPFILEHEADER數據結構,可以獲得一些位圖的信息,包括
info
->bmiHeader
.biWidth
,
info
->bmiHeader
.biHeight
,
info
->bmiHeader
.biBitCount
,
info
->bmiHeader
.biSizeImage
,
圖片文件的讀取
我自己手寫的函數,別問,問就是查文檔。
char* readpic(const char *filename
)
{HFILE pic
= _lopen(filename
, OF_READ
);int size
= GetFileSize((HANDLE
)pic
, NULL);char *buf
= (char*)malloc(size
);_lread(pic
, buf
, size
); _lclose(pic
);return buf
;
}
返回值buf即為圖片在內存中的首地址,記得要釋放它。
3. 淡入淡出原理
使用一張全黑圖片A和一張待顯示的圖片B,顯示度fade表示每個點的像素顏色 = A*(1-fade)+B*fade。讓fade從0變化到1即可實現淡入效果,淡出同理。
簡單來說,需要把每個點的像素組合起來,對每個字節進行上邊的運算。
4. MMX
MMX采用處理器的80位的浮點寄存器的低64位作為MMX寄存器,一共有8個,從mm0到mm7,因為是“借用”浮點寄存器的低64位所以每次在用完MMX指令后一定要用EMMS指令將寄存器清空,MMX主要是針對整數運算進行優化,一個64位的MMX寄存器可以同時存入8個8位或者4個16位的整數,估計一次性就可以完成8次8位運算或者4次16位運算,要注意的MMX指令不能直接對32位數進行2次運算,但可以把32位拆分成兩個16位再進行運算。MMX技術還有一個非常有用的特性是飽和運算,比如兩個8位數相加:128+129相加后很明顯超過了8位的最大值256,但是進行飽和運算相加的結果將是最大值256,飽和運算將運算結果控制在相應位數的范圍內。
MMX指令集的優勢在于,它可以對64位的寄存器的每16位并行計算,只需要一個時鐘周期。
指令集不列舉了,可以查看參考資料。
項目代碼
完整的項目文件可以直接從我的github上下載(記得點個star)。下面是核心代碼:
編譯環境是MSVC,可以直接用VS或者VC,也可以參考【筆記】C++獨立MSVC編譯配置(命令行+sublime)進行配置。
#include <windows.h>
#include <stdio.h>const char *filename
[] = {"../pic/black-1920-1080-32.bmp","../pic/back-1920-1080-32.bmp"
};
const int WIDTH
= 1920, HEIGHT
= 1080, BITS
= 32;
int filesize
;BYTE
*readpic(const char *filename
)
{HFILE pic
= _lopen(filename
, OF_READ
);filesize
= GetFileSize((HANDLE
)pic
, NULL);BYTE
*buf
= (BYTE
*)malloc(filesize
);_lread(pic
, buf
, filesize
); _lclose(pic
);return buf
;
}void n_mmx(BYTE
*p1
, BYTE
*p2
, BYTE
*ptar
, int size
, int fade
)
{while(size
--){int res
= (*p2
) - (*p1
);res
= (res
* fade
) >> 8; res
+= *p1
; *ptar
= res
;++p1
, ++p2
, ++ptar
;}
}void y_mmx(BYTE
*pic1
,BYTE
*pic2
,BYTE
*pic
,int size
,int fade
)
{size
/= 4;__int32
*p1
= (__int32
*)(pic1
);__int32
*p2
= (__int32
*)(pic2
);__int32
*ptar
= (__int32
*)(pic
);__int16 fade1
[4] = {255-fade
, 255-fade
, 255-fade
, 255-fade
};__int16 fade2
[4] = {fade
, fade
, fade
, fade
};_asm
{movq mm3
, [fade1
]; movq mm4
, [fade2
];}for (unsigned int i
= 0; i
< size
; ++i
){__asm
{pxor mm0
, mm0 mov esi
, p1 mov edx
, p2 mov edi
, ptarmovd mm1
, [esi
] movd mm2
, [edx
] punpcklbw mm1
, mm0 punpcklbw mm2
, mm0 pmullw mm1
, mm3pmullw mm2
, mm4paddw mm1
, mm2 psrlw mm1
, 8 packuswb mm1
, mm0 movd
[edi
], mm1
}++p1
, ++p2
, ++ptar
;}_asm EMMS
}int main(void)
{BYTE
*buf1
= readpic(filename
[0]), *buf2
= readpic(filename
[1]);BYTE
*buf
= (BYTE
*)malloc(filesize
);HDC hdc
= GetDC(NULL);BITMAPINFO
*info
= (BITMAPINFO
*)(buf
+ 0x0e);for(int mmx
=0;mmx
<2;++mmx
){memcpy(buf
,buf1
,filesize
);DWORD start_time
= GetTickCount();for(int step
=-50;step
<556;step
+=2) {int fade
;if(step
<0) fade
= 0;else if(step
>=300) fade
= 555 - step
;else if(step
>=256) fade
= 256;else fade
= step
;if(mmx
) y_mmx(buf1
+0x36, buf2
+0x36, buf
+0x36, info
->bmiHeader
.biSizeImage
, fade
);else n_mmx(buf1
+0x36, buf2
+0x36, buf
+0x36, info
->bmiHeader
.biSizeImage
, fade
);SetDIBitsToDevice(hdc
,0, 0, WIDTH
, HEIGHT
, 0, 0, 0, HEIGHT
, buf
+ 0x36, info
, DIB_RGB_COLORS
);}DWORD end_time
= GetTickCount();printf("%s method spend %d ms.\n",mmx
?"MMX":"Normal",(end_time
- start_time
)); }ReleaseDC(NULL, hdc
);free(buf1
), free(buf2
), free(buf
);return 0;
}
總結
以上是生活随笔為你收集整理的【笔记】COA课内实验-MMX指令集的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。