• BMP RGB888转RGB565 +上下翻转+缩放


     
    典型的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 != 24)//图像的位数
        {
            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(),0,0,rect.Width(),rect.Height(),0,0,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 = 0;
            ::GetFileSizeEx(bmpFile, &liSize);
    
            int nBitmapSize = abs(iBmpHeight) * WIDTHBYTES(iBmpWidth * bmpInfo.biBitCount);
            if(bmpInfo.biPlanes != 1)
            {
                break;
            }
            if(bmpInfo.biBitCount != 1 && bmpInfo.biBitCount != 4 && bmpInfo.biBitCount != 8 && bmpInfo.biBitCount != 16 && bmpInfo.biBitCount != 24 && bmpInfo.biBitCount != 32)
            {
                break;
            }
            if(bmpInfo.biCompression != BI_RGB && bmpInfo.biCompression != BI_BITFIELDS)
            {
                break;
            }
            if(bmpInfo.biWidth <= 0 || bmpInfo.biHeight == 0)
            {
                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 < 16)
            {
                //...
            }
            else if(bmpInfo.biBitCount == 16)
            {
                //...
            }
            else if(bmpInfo.biBitCount == 24)
            {
                ::SetFilePointer(bmpFile, bmpHeader.bfOffBits, NULL, SEEK_SET);
    
                BYTE *pData;
                pData = new BYTE[nBitmapSize];
    
                DWORD dwByteRead = 0;
                dwByteRead = 0;
                ::ReadFile(bmpFile, pData, nBitmapSize, &dwByteRead, NULL);
    
                //pR, pG, pB是临时指针
                BYTE *pR = m_pR;
                BYTE *pG = m_pG;
                BYTE *pB = m_pB;
    
                for(int j = 0; j < abs(bmpInfo.biHeight); j++)
                {
                    BYTE *pTemp = pData + WIDTHBYTES(bmpInfo.biWidth * bmpInfo.biBitCount) * j;
                    for(int i = 0; i < bmpInfo.biWidth; i++)
                    {
                        *pB++ = *pTemp++;
                        *pG++ = *pTemp++;
                        *pR++ = *pTemp++;
                    }
                }
    
                delete[] pData;
            }
            else if(bmpInfo.biBitCount == 32)
            {
                //...
            }

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

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

    4.  缩放 

    /* new_heigth和new_width为目标图片的大小,可自定义 */
        int new_height = 430;
        int new_width = 160;
        int newSize = new_width*new_height*2;
        length = newSize;
        BYTE * newData = new BYTE[newSize];
        /* 分配新的内存 */
        memset(newData, 0, new_width*new_height*2);
    
        for(int h = 0; h < image_height; h++)
            for (int w = 0; 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)*2] = Tmp;
                newData[(x*new_width + y)*2 + 1] = pData[iCoordM+1];
            }

    (完)

  • 相关阅读:
    【2021-01-07】叫我“何工”就好
    2021年的 目标计划
    转:我在拼多多的三年 https://www.leadroyal.cn/?p=1228#more-1228
    匀强电场等效重力场
    开源图片素材管理软件-civet
    心学#传习录学习
    工作感受月记202101月
    统计字典序元音字符的数量
    可被5整除的二进制
    假期刷题--JAVA
  • 原文地址:https://www.cnblogs.com/fwst/p/3819821.html
Copyright © 2020-2023  润新知