日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【笔记】COA课内实验-MMX指令集

發(fā)布時間:2023/12/3 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【笔记】COA课内实验-MMX指令集 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

參考資料
關于BMP文件格式的詳解
SetDIBitsToDevice函數(shù)
SetDIBitsToDevice function | Microsoft docs
BITMAPINFO structure
BITMAPINFOHEADER structure
MMX匯編指令優(yōu)化
MMX指令集系列之三----數(shù)據(jù)飽和壓縮與重排指令

原理學習

1. BMP-DIB位圖編碼

  • 什么是BMP
    BMP(全稱Bitmap,位圖)是Windows中的標準圖像文件格式,其中有一類叫做設備無關位圖(DIB)。
    BMP文件存儲數(shù)據(jù)時,圖像的掃描方式是按從下到上,從左到右的順序。
    BMP文件按深度分為多類,如16色,256色,24位,32位,以下均按32位位圖。
    BMP文件編碼依次為:位圖文件頭+位圖信息頭+調(diào)色板+數(shù)據(jù)區(qū)。(32位位圖沒有調(diào)色板)
  • 位圖信息編碼
    僅列舉需要用到的編碼,單位為字節(jié)(8位)
    0x02-0x05 表示整個文件的大小。(注意要把每個區(qū)域按字節(jié)倒過來讀,即0x05,0x04,0x03,0x02,下同)
    0x0A-0x0D 表示從文件開始到數(shù)據(jù)區(qū)的偏移量
    0x12-0x15 位圖寬度(單位為像素,下同)
    0x16-0x19 位圖高度
    0x1C-0x1D 像素位數(shù),24位即0x18
    0x22-0x25 數(shù)據(jù)區(qū)的大小
  • 位圖數(shù)據(jù)編碼
    一般從0x36開始。
    32位位圖一個像素正好需要四個字節(jié),從左到右4個字節(jié)分別是BGRA,即藍,綠,紅,透明。
    32位位圖不需要強制對齊,因為它本身就四字節(jié)對齊。
  • 2. 繪圖API

  • GetDC function
  • HDC GetDC(HWND hWnd );

    得到一個DeviceContext句柄對象,可以在上面畫像素,調(diào)用時輸入null即可。

  • SetDIBitsToDevice function
    使用來自圖片的顏色數(shù)據(jù)來設置目標設備矩形區(qū)域上的像素
  • int SetDIBitsToDevice(HDC hdc, //目標區(qū)域句柄int xDest, //目標區(qū)域左上角的橫坐標int yDest, //目標區(qū)域左上角的縱坐標DWORD w, //圖片寬度DWORD h, //圖片高度int xSrc, //圖片左下角的橫坐標int ySrc, //圖片左下角的縱坐標UINT StartScan, //掃描線開始的位置UINT cLines, //掃描線個數(shù)const VOID *lpvBits, //指向位圖數(shù)據(jù)部分的指針,一般為位圖首地址+0x36const BITMAPINFO *lpbmi, //指向位圖信息頭部分的指針,一般為位圖首地址+0x0eUINT ColorUse //表明顏色板種類,可選的有DIB_PAL_COLORS(16位顏色索引),DIB_RGB_COLORS(RGB逐字顏色) );
  • BITMAPINFO 數(shù)據(jù)結(jié)構
    內(nèi)部還有一個BITMAPFILEHEADER數(shù)據(jù)結(jié)構,可以獲得一些位圖的信息,包括
  • info->bmiHeader.biWidth, //位圖寬度 info->bmiHeader.biHeight, //位圖高度 info->bmiHeader.biBitCount, //顏色位數(shù) info->bmiHeader.biSizeImage, //實際占用的字節(jié)數(shù),注意這里會考慮字節(jié)對齊
  • 圖片文件的讀取
    我自己手寫的函數(shù),別問,問就是查文檔。
  • 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即為圖片在內(nèi)存中的首地址,記得要釋放它。

    3. 淡入淡出原理

    使用一張全黑圖片A和一張待顯示的圖片B,顯示度fade表示每個點的像素顏色 = A*(1-fade)+B*fade。讓fade從0變化到1即可實現(xiàn)淡入效果,淡出同理。
    簡單來說,需要把每個點的像素組合起來,對每個字節(jié)進行上邊的運算。

    4. MMX

    MMX采用處理器的80位的浮點寄存器的低64位作為MMX寄存器,一共有8個,從mm0到mm7,因為是“借用”浮點寄存器的低64位所以每次在用完MMX指令后一定要用EMMS指令將寄存器清空,MMX主要是針對整數(shù)運算進行優(yōu)化,一個64位的MMX寄存器可以同時存入8個8位或者4個16位的整數(shù),估計一次性就可以完成8次8位運算或者4次16位運算,要注意的MMX指令不能直接對32位數(shù)進行2次運算,但可以把32位拆分成兩個16位再進行運算。MMX技術還有一個非常有用的特性是飽和運算,比如兩個8位數(shù)相加:128+129相加后很明顯超過了8位的最大值256,但是進行飽和運算相加的結(jié)果將是最大值256,飽和運算將運算結(jié)果控制在相應位數(shù)的范圍內(nèi)。

    MMX指令集的優(yōu)勢在于,它可以對64位的寄存器的每16位并行計算,只需要一個時鐘周期。
    指令集不列舉了,可以查看參考資料。

    項目代碼

    完整的項目文件可以直接從我的github上下載(記得點個star)。下面是核心代碼:
    編譯環(huán)境是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; // p2*fade/256 - p1*fade/256res += *p1; // p2*fade/256 + p1*(1-fade/256)*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]; // 00RR 00RR 00RR 00RRmovq mm4, [fade2];}/* 不能先減再乘除,可能產(chǎn)生負數(shù) */for (unsigned int i = 0; i < size; ++i){__asm{pxor mm0, mm0 // 清除mm0//將所需數(shù)據(jù)移入寄存器mov esi, p1 mov edx, p2 mov edi, ptarmovd mm1, [esi] // UUUU UUUU XXXX XXXXmovd mm2, [edx] // UUUU UUUU YYYY YYYY//將mm1和mm2解開,構成 00XX 00XX 00XX 00XX形式punpcklbw mm1, mm0 // 00XX 00XX 00XX 00XXpunpcklbw mm2, mm0 // 00YY 00YY 00YY 00YYpmullw mm1, mm3pmullw mm2, mm4paddw mm1, mm2 // (p1*(255-fade) + p2*fade) psrlw mm1, 8 // 00ZZ 00ZZ 00ZZ 00ZZpackuswb mm1, mm0 // 0000 0000 ZZZZ ZZZZ//將結(jié)果傳回目標位置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, /* 目標區(qū)域左上角坐標,圖片長寬 */0, 0, 0, HEIGHT, /* 圖片左下角坐標,開始行數(shù),有效行數(shù) */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; }

    總結(jié)

    以上是生活随笔為你收集整理的【笔记】COA课内实验-MMX指令集的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。