前言

参考资料
关于BMP文件格式的详解
SetDIBitsToDevice函数
SetDIBitsToDevice function | Microsoft docs
BITMAPINFO structure
BITMAPINFOHEADER structure
MMX汇编指令优化
MMX指令集系列之三----数据饱和压缩与重排指令

原理学习

1. BMP-DIB位图编码

  1. 什么是BMP
    BMP(全称Bitmap,位图)是Windows中的标准图像文件格式,其中有一类叫做设备无关位图(DIB)。
    BMP文件存储数据时,图像的扫描方式是按从下到上,从左到右的顺序。
    BMP文件按深度分为多类,如16色,256色,24位,32位,以下均按32位位图。
    BMP文件编码依次为:位图文件头+位图信息头+调色板+数据区。(32位位图没有调色板)
  2. 位图信息编码
    仅列举需要用到的编码,单位为字节(8位)
    0x02-0x05 表示整个文件的大小。(注意要把每个区域按字节倒过来读,即0x05,0x04,0x03,0x02,下同)
    0x0A-0x0D 表示从文件开始到数据区的偏移量
    0x12-0x15 位图宽度(单位为像素,下同)
    0x16-0x19 位图高度
    0x1C-0x1D 像素位数,24位即0x18
    0x22-0x25 数据区的大小
  3. 位图数据编码
    一般从0x36开始。
    32位位图一个像素正好需要四个字节,从左到右4个字节分别是BGRA,即蓝,绿,红,透明。
    32位位图不需要强制对齐,因为它本身就四字节对齐。

2. 绘图API

  1. GetDC function
HDC GetDC(HWND hWnd
);

得到一个DeviceContext句柄对象,可以在上面画像素,调用时输入null即可。

  1. 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,   //指向位图数据部分的指针,一般为位图首地址+0x36const BITMAPINFO *lpbmi,     //指向位图信息头部分的指针,一般为位图首地址+0x0eUINT             ColorUse   //表明颜色板种类,可选的有DIB_PAL_COLORS(16位颜色索引),DIB_RGB_COLORS(RGB逐字颜色)
);
  1. BITMAPINFO 数据结构
    内部还有一个BITMAPFILEHEADER数据结构,可以获得一些位图的信息,包括
info->bmiHeader.biWidth,          //位图宽度
info->bmiHeader.biHeight,            //位图高度
info->bmiHeader.biBitCount,      //颜色位数
info->bmiHeader.biSizeImage,     //实际占用的字节数,注意这里会考虑字节对齐
  1. 图片文件的读取
    我自己手写的函数,别问,问就是查文档。
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;     // 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];}/* 不能先减再乘除,可能产生负数 */for (unsigned int i = 0; i < size; ++i){__asm{pxor       mm0, mm0            // 清除mm0//将所需数据移入寄存器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//将结果传回目标位置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指令集相关推荐

  1. 【数据库系统原理】数据库课内实验

    说明:这是武汉理工大学计算机学院[数据库系统原理]课程课内实验. >>点击查看武汉理工大学计算机专业课程资料汇总 >>点击查看WUTer计算机专业实验汇总 谨记:纸上得来终觉浅 ...

  2. 武汉理工大学操作系统 课内实验

    文章目录 前言 主要仪器设备及耗材 一.动态分区管理 实验内容描述 实验基本原理与设计 二.磁盘调度 实验内容描述 实验基本原理与设计 总结 前言 操作系统课内实验有两个,验收的学姐人贼好,只看了验收 ...

  3. 武汉理工大学数值分析课内实验

    文章目录 前言 主要仪器设备及耗材 一.用C语言实现几个多项式插值的程序.(Lagrange插值.Newton插值) 实验内容描述 实验基本原理与设计 分析与设计 实验结果 二.用C语言实现几个求常微 ...

  4. webservice-UML课内实验报告实验三

    1.webservice 现将网上关于webservice的讲解提炼出来,通过一个最简单使用并且方便的例子,告诉大家什么是webservice. 简单来说,webservice就是远程调用技术,也叫X ...

  5. 计算机组成与结构课内实验:16位模型机的设计

    我们当时是有两个实验的.一个是计组课内的实验:16位模型机的设计.还有一个是计组的最终大课设:计算机组成与结构综合实验,另一篇文章我将给出综合实验的报告 第一个是课内的设计实验: 引言 1.1 设计目 ...

  6. 计算机组成原理课内实验,【计算机基础论文】计算机组成原理课程实验教学改革(共2885字)...

    摘要:目前<计算机组成原理>的实验内容存在与课程定位目标相悖的问题,一味追求实验内容的复杂性,而忽视了计算机组成原理实验教学的完整性和概念性.针对上述问题,本文对<计算机组成原理&g ...

  7. 课内实验记录|信用卡号的合法性检查

    题目要求 (附加题6.31 信用卡号的合法性,可选做) 信用卡号遵循下面的模式.一个信用卡号必须是13-16位的整数.它的开头必须是: 4,指visa卡 5,指master卡 37,指American ...

  8. 转载自---课内实验记录|信用卡号的合法性检查 2019年05月05日 21:37:45 @退堂鼓一级演员

    题目要求 (附加题6.31 信用卡号的合法性,可选做) 信用卡号遵循下面的模式.一个信用卡号必须是13-16位的整数.它的开头必须是: 4,指visa卡 5,指master卡 37,指American ...

  9. 计算机网络基础课内实验报告答案,计算机网络基础课内实验报告-20210418131414.docx-原创力文档...

    <计算机网络基础>课内实验 学部:经济与管理学院 专业:市场营销(网络营销) 班级: 学号 : 姓名 : 指导教师 :唐芳萍 2016年 6月 21日 实验一 用双绞线制作网线(3 课时) ...

最新文章

  1. The 2014 ACM-ICPC Asia Mudanjiang Regional First Round C
  2. 初识【jQuery】,入门必看!
  3. uml+oopc嵌入式c语言开发精讲_新的程序开发模式出现,传统的嵌入式C语言程序员快要灭绝了?...
  4. 数据分析Power BI数据建模教程(三)——如何优化数据模型
  5. objective-c block 讲解
  6. Google Cloud资源层级, IAM Identity and Access Management, 控制台云交互
  7. 汇编环境搭建 Windows10 VS2019 MASM32
  8. DirectX修复工具(DirectX Repair)修复工具V4.0增强版
  9. 南京工程学院《DSP技术及应用》期末试卷
  10. 智能优化算法——布谷鸟搜索算法原理(附代码)
  11. 自己动手修理单击变双击的鼠标
  12. 上海交通大学2004年数学分析考研试题
  13. 苹果电脑 / Mac 开机密码忘记了应该如何操作?
  14. GoLand No Tests Were Run : 不能使用 fmt.Printf() BUG
  15. UE5笔记【四】UE5主材质Master Materials和材质实例MI
  16. 全球及中国雾化铜基粉末行业运营状况与发展动态分析报告2022-2028年
  17. 【译学】数据分析手册学习01: 导言、学习目标、指导原则
  18. sim卡没坏但苹果手机无服务_苹果6sp无服务信号不好解决办法
  19. fedora下载工具
  20. 修改win7开机登陆界面背景图片

热门文章

  1. DBeaver SQL format 第三方插件方案
  2. networkx网络拓扑节点图和树,python
  3. opencv程序十一:鼠标绘图
  4. GLES2.0中文API-glTexImage2D
  5. C# 路径 目录 文件操作办法
  6. 群狼调研开展电器店神秘顾客暗访违规稽核项目
  7. Android加密篇 RSA
  8. 20071011听力原文
  9. S32DS Components组件配置
  10. 【老姐学PHP】PHP框架lavarel之Artisan命令