典型的BMP图像文件由四部分组成:
(1) 位图头文件数据结构,它包含BMP图像文件的类型、文件大小和位图起始位置等信息;
  

typedef struct tagBITMAPFILEHEADER {
WORD bfType;//位图文件的类型,必须为BM(1-2字节)
DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)
WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)
WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)
DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)
} BITMAPFILEHEADER;
(2) 位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;//本结构所占用字节数(15-18字节)
LONG biWidth;//位图的宽度,以像素为单位(19-22字节)
LONG biHeight;//位图的高度,以像素为单位(23-26字节)
WORD biPlanes;
WORD biBitCount;//每个像素所需的位数,必须是1(双色),(29-30字节) //4(16色),8(256色)16(高彩色)或24(真彩色)之一
DWORD biCompression;
DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
(3) 调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;
(4) 位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。
 
1. 打开位图并显示
    BITMAPFILEHEADER bmpHeader;//文件头
BITMAPINFOHEADER bmpInfo;//信息头 CFileDialog dlg(TRUE, "*.BMP", NULL, NULL,"位图文件(*.BMP)|*.bmp;*.BMP|",this);
CFile bmpFile;//记录打开文件
CString strFileName;//记录选择文件路径
if (!dlg.DoModal() == IDOK) return;
strFileName = dlg.GetPathName(); //以只读的方式打开文件
if(!bmpFile.Open(strFileName, CFile::modeRead|CFile::typeBinary)) return; //读取文件头到bmpHeader
if (bmpFile.Read(&bmpHeader,sizeof(BITMAPFILEHEADER)) != sizeof(BITMAPFILEHEADER))
{
AfxMessageBox("read bmp header failed!");
return;
} /*0x4d42=’BM’,表示是Windows支持的BMP格式。
(注意:查ascii表B 0x42,M0x4d,bfType 为两个字节,B为low字节,M为high字节
所以bfType=0x4D42,而不是0x424D
*/
if (bmpHeader.bfType != 0x4d42)
{
AfxMessageBox("invalid file type!");
return;
} //读取文件信息头bmpInfo
if (bmpFile.Read(&bmpInfo,sizeof(BITMAPINFOHEADER)) != sizeof(BITMAPINFOHEADER))
{
AfxMessageBox("read bmp infor header failed!");
return;
}
//确认是24位位图
if (bmpInfo.biBitCount != )//图像的位数
{
AfxMessageBox("File is not 24 bit.Application doesn't support this kind of file!");
return;
}
/*
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
*/
pBmpInfo = (BITMAPINFO *)new char[sizeof(BITMAPINFOHEADER)];
if (!pBmpInfo)
{
AfxMessageBox("memory error!");
return;
}
//为图像数据申请空间
memcpy(pBmpInfo, &bmpInfo, sizeof(BITMAPINFOHEADER));
//计算颜色表区域大小:结构体的大小(包含颜色表)-颜色数据的偏移量
DWORD dataBytes = bmpHeader.bfSize - bmpHeader.bfOffBits;
pBmpData = (BYTE*)new char[dataBytes];
if (!pBmpData)
{
AfxMessageBox("memory error!");
delete pBmpData;
return;
}
if (bmpFile.Read(pBmpData,dataBytes) != dataBytes)
{
AfxMessageBox("Read bmp data failed!");
delete pBmpInfo;
delete pBmpData;
return;
}
//bmpFile.Close(); CWnd *pWnd=GetDlgItem(IDC_IMAGE);//获得pictrue控件窗口的句柄
CRect rect;
pWnd->GetClientRect(&rect);//获得pictrue控件所在的矩形区域
CDC *pDC=pWnd->GetDC();//获得pictrue控件的DC
//显示图片
pDC->SetStretchBltMode(COLORONCOLOR); StretchDIBits(pDC->GetSafeHdc(),,,rect.Width(),rect.Height(),,,bmpInfo.biWidth,bmpInfo.biHeight,pBmpData,pBmpInfo,DIB_RGB_COLORS,SRCCOPY); iBmpWidth = bmpInfo.biWidth;
iBmpHeight = bmpInfo.biHeight;

2. 将24位图转化为16位位图(从RGB888到RGB565)

需要将原来的颜色表数据分离成R,G,B三组,然后R舍弃3位,G舍弃2位,B舍弃3位。

a. 分离,读取RGB数据存到三个BYTE*数组里m_pR, m_pG, m_pB;

     LARGE_INTEGER liSize;
liSize.QuadPart = ;
::GetFileSizeEx(bmpFile, &liSize); int nBitmapSize = abs(iBmpHeight) * WIDTHBYTES(iBmpWidth * bmpInfo.biBitCount);
if(bmpInfo.biPlanes != )
{
break;
}
if(bmpInfo.biBitCount != && bmpInfo.biBitCount != && bmpInfo.biBitCount != && bmpInfo.biBitCount != && bmpInfo.biBitCount != && bmpInfo.biBitCount != )
{
break;
}
if(bmpInfo.biCompression != BI_RGB && bmpInfo.biCompression != BI_BITFIELDS)
{
break;
}
if(bmpInfo.biWidth <= || bmpInfo.biHeight == )
{
break;
}
if(bmpHeader.bfOffBits + nBitmapSize > liSize.QuadPart)
{
break;
} //m_pR,m_pG,m_pB位BYTE *;
m_pR = new BYTE[bmpInfo.biWidth * abs(bmpInfo.biHeight)];
m_pG = new BYTE[bmpInfo.biWidth * abs(bmpInfo.biHeight)];
m_pB = new BYTE[bmpInfo.biWidth * abs(bmpInfo.biHeight)]; if(bmpInfo.biBitCount < )
{
//...
}
else if(bmpInfo.biBitCount == )
{
//...
}
else if(bmpInfo.biBitCount == )
{
::SetFilePointer(bmpFile, bmpHeader.bfOffBits, NULL, SEEK_SET); BYTE *pData;
pData = new BYTE[nBitmapSize]; DWORD dwByteRead = ;
dwByteRead = ;
::ReadFile(bmpFile, pData, nBitmapSize, &dwByteRead, NULL); //pR, pG, pB是临时指针
BYTE *pR = m_pR;
BYTE *pG = m_pG;
BYTE *pB = m_pB; for(int j = ; j < abs(bmpInfo.biHeight); j++)
{
BYTE *pTemp = pData + WIDTHBYTES(bmpInfo.biWidth * bmpInfo.biBitCount) * j;
for(int i = ; i < bmpInfo.biWidth; i++)
{
*pB++ = *pTemp++;
*pG++ = *pTemp++;
*pR++ = *pTemp++;
}
} delete[] pData;
}
else if(bmpInfo.biBitCount == )
{
//...
}

b. 转化,从24位转化成16位

//新文件的头信息
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih; memset(&bmfh, , sizeof(bmfh));
memset(&bmih, , sizeof(bmih)); int nBitmapSize = abs(bmpInfo.biHeight) * WIDTHBYTES(bmpInfo.biWidth * );
length = nBitmapSize;
bmfh.bfType = 'MB';
bmfh.bfOffBits = sizeof(bmfh) + sizeof(bmih) + ;
bmfh.bfSize = bmfh.bfOffBits + nBitmapSize; bmih.biSize = sizeof(bmih);
bmih.biWidth = bmpInfo.biWidth;
bmih.biHeight = bmpInfo.biHeight;
bmih.biPlanes = ;
bmih.biBitCount = ;
bmih.biCompression = BI_BITFIELDS;
bmih.biSizeImage = nBitmapSize; /* 转化后的颜色表保存在pData中 */
BYTE *pData;
pData = new BYTE[nBitmapSize];
memset(pData, , nBitmapSize); myData = new char[nBitmapSize];
memset(myData,,nBitmapSize); char * inverseData = new char[nBitmapSize];
memset(inverseData, , nBitmapSize); BYTE *pR = m_pR;
BYTE *pG = m_pG;
BYTE *pB = m_pB; /* 以下是转化的核心代码 */
/* 转化过程图像的宽和高是不变的,变得是每个像素点从3个字节变成了2个字节 */
for(int j = ; j < abs(bmih.biHeight); j++)
{
/* 临时指针pTemp 每次从新的一行开始 */
WORD *pTemp = (WORD *)(pData + WIDTHBYTES(bmih.biWidth * ) * j); for(int i = ; i < bmih.biWidth; i++)
{
#if 0
*pTemp++ = ((WORD)(*pR++ << ) & 0xf800) | ((WORD)(*pG++ << ) & 0x07e0) | ((WORD)(*pB++ >> ) & 0x001f);
#else
/* 分别去掉低3,2,3位 */
int nR = (*pR++ + ) >> ;
int nG = (*pG++ + ) >> ;
int nB = (*pB++ + ) >> ;
/* nR位5位,不能超过31,nG为6位,不能超过63,nB同nR */
if(nR > ) nR = ;
if(nG > ) nG = ;
if(nB > ) nB = ;
/* 将新的R,G,B数据拼到2个字节里,比例为5:6:5 */
*pTemp++ = (nR << ) | (nG << ) | nB;
#endif
}
}
3. 将图片上下对称翻转。图像点阵是image_width * image_height 的大小,翻转时只需要将 上下两半对应的位置的点对换就行了。经上面的转换后,图像中每个点占2个字节了,所以每个点换两个字节就行。
  int image_width = bmih.biWidth;
int image_height = bmih.biHeight;
int index = ;//表示16色,占2个字节
for(int h = ; h < image_height/; h++)
for (int w = ; w < image_width; w++)
{
/* iCoordM 和 iCoordN分别是上下对称的点在颜色表字节数组中的坐标,
交换iCoordM位置和iCoordM+1位置2个字节就行了。
*/
const int iCoordM = index*(h*image_width + w);
const int iCoordN = index*((image_height - h -)*image_width + w);
BYTE Tmp = pData[iCoordM];
pData[iCoordM] = pData[iCoordN];
pData[iCoordN] = Tmp;
Tmp = pData[iCoordM+];
pData[iCoordM + ] = pData[iCoordN + ];
pData[iCoordN + ] = Tmp;
/*如果是24位图像,就加上下面的内容,就是再交换一个字节*/
/*Tmp = pData[iCoordM + 2];
pData[iCoordM + 2] = pData[iCoordN + 2];
pData[iCoordN + 2] = Tmp;*/
}

4.  缩放

/* new_heigth和new_width为目标图片的大小,可自定义 */
int new_height = ;
int new_width = ;
int newSize = new_width*new_height*;
length = newSize;
BYTE * newData = new BYTE[newSize];
/* 分配新的内存 */
memset(newData, , new_width*new_height*); for(int h = ; h < image_height; h++)
for (int w = ; w < image_width; w++)
{
/* 计算每个像素的起始位置 */
const int iCoordM = index * (h * image_width + w);
//const int iCoordN = index*((image_height - h -1)*image_width + w);
BYTE Tmp = pData[iCoordM];
int x = int( double(h)/image_height * new_height);//新行数
int y = int( double(w)/image_width * new_width);//新列数 /* 将原来图片的每个像素按比例位置映射到新的图片上
原来的位置是(w, h),通过比例就可以计算出新的位置是
(int(double(w)/image_width*new_width), int(double(h)/image_height*new_height)),
然后将新的位置开始的2个字节等于原来位置的2个字节,就完成了缩放。
*/
newData[(x*new_width + y)*] = Tmp;
newData[(x*new_width + y)* + ] = pData[iCoordM+];
}

(完)

BMP RGB888转RGB565 +上下翻转+缩放的更多相关文章

  1. 使用NEON指令加速RGB888和RGB565的相互转换

    最近在做一个项目需要将RGB888转换为RGB565,用C语言转换的代码很简单,这是从ffmpeg中摘抄的代码 static inline void rgb24to16_c(const uint8_t ...

  2. 19_Android中图片处理原理篇,关于人脸识别站点,图片载入到内存,图片缩放,图片翻转倒置,网上撕衣服游戏案例编写

    1载入图片到内存 (1).数码相机照片特别是大于3m以上的,内存吃不消,会报OutOfMemoryError,若是想仅仅显示原图片的1/8,能够通过BitmapFactory.Options来实现.详 ...

  3. 19_Android中图片处理原理篇,关于人脸识别网站,图片加载到内存,图片缩放,图片翻转倒置,网上撕衣服游戏案例编写

    1加载图片到内存 (1).数码相机照片特别是大于3m以上的,内存吃不消,会报OutOfMemoryError,若是想只显示原图片的1/8,可以通过BitmapFactory.Options来实现,具体 ...

  4. RGB565的理解

    一个彩色图像由R G B三个分量组成,一个RGB565的每一个像素点数据为2Byte,即16位,那么从名字上就可看出来这16位中,高5位为R分量,中间6位为G分量,低5位为B分量. 下面做了一个实验, ...

  5. Beginning SDL 2.0(3) SDL介绍及BMP渲染

    SDL是一个跨平台的多媒体库.为了实现跨平台,SDL提供了一个简单的界面库抽象,比如提供了SDL_Window用于表示窗口句柄,SDL_Surface.SDL_Texture.SDL_Renderer ...

  6. RGB565的转换

    RGB色彩模式也就是“红绿蓝”模式是一种颜色标准,是通过对红(R).绿(G).蓝(B)三种颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红.绿.蓝三个通道的颜色,这个标准几 ...

  7. STM32单片机图片解码

    图片解码首先是最简单的bmp图片解码,关于bmp的结构可自行查阅,代码如下 #ifndef __BMPDECODE_H_ #define __BMPDECODE_H_ #include "f ...

  8. Linux LCD 显示图片【转】

    转自:https://blog.csdn.net/niepangu/article/details/50528190 BMP和JPEG图形显示程序1)  在LCD上显示BMP或JPEG图片的主流程图首 ...

  9. TJpgDec—轻量级JPEG解码器

    TJpgDec-轻量级JPEG解码器 本文由乌合之众lym瞎编,欢迎转载blog.cnblogs.net/oloroso 下文中解码一词皆由decompression/decompress翻译而来. ...

随机推荐

  1. Android与JS交互,json传参问题

    一.JS调用Android的方法 JS调用安卓的方法,并且传递的参数为json格式的字符串(JSONObject.toString()), 例如: var json = {"name&quo ...

  2. mysql密码设置为空怎么办?

    由于很多童鞋安装使用MySQL时,安装时没有设置密码,或者像我一样图省事设置密码为空,想为其设置新密码: 1.点击  开始------>运行----在弹出的对话框中输入cmd 如下图: 2.使用 ...

  3. Linux云服务器磁盘不见了?解决方案在这里,云服务器磁盘挂载

    用过诸多种云以后,发现有个通病,就是新买的数据盘在机器中找不到.本篇总结一下此类问题的解决方法,望各位点赞,有问题评论区见 一.云服务和物理机一样,你买了云服务器的数据盘以后,就相当于把数据盘直接安装 ...

  4. JAVA 基础编程练习题39 【程序 39 分数累加】

    39 [程序 39 分数累加] 题目:编写一个函数,输入 n 为偶数时,调用函数求 1/2+1/4+...+1/n,当输入 n 为奇数时,调用函数 1/1+1/3+...+1/n package cs ...

  5. Django -- DateTimeField

    默认为时区时间时,需要导入django内置的timezone模块 from django.utils import timezone create_at = models.DateTimeField( ...

  6. [Python[Anaconda & PyTorch]] -- 使用conda 安装 Torch 出现错误 --Windows

    ... (⊙o⊙)… ... 当时具体的错误我没有截图, 用这个命令时 , conda 会报无法在源中找到PyTorch, 还是什么的错误 有很大的一个可能是, 安装的Anaconda 是32 位的, ...

  7. FFMPEG 常用命令行

    目录 1. 分离音视频 2. 解复用 3. 视频转码 4. 视频封装 5. 视频剪切 6. 视频录制 7.叠加水印 8.将MP3转换为PCM数据 9. 推送RTP流.接收RTP流并存为ts文件 10. ...

  8. Windows下遍历所有GIT目录更新项目脚本

    将下面代码保存为.bat文件 @echo off set cdir=%~dp0 for /f "delims=" %%i in ('dir /ad/b/s "%cdir% ...

  9. [转帖]XCopy复制文件夹命令及参数详解以及xcopy拷贝目录并排除特定文件

    XCopy复制文件夹命令及参数详解以及xcopy拷贝目录并排除特定文件 https://www.cnblogs.com/smartsmile/p/7665979.html xcopy dirA dir ...

  10. SQL SERVER 根据字段名称批量设置为主键

    --设置主键 --alter table 你的表名 add constraint pk_s primary key (id) SELECT 'alter table ' + TABLE_NAME + ...