代码之家  ›  专栏  ›  技术社区  ›  A191919

C++将32位BMP图像转换为24位BMP和16位BMP

  •  1
  • A191919  · 技术社区  · 8 年前

    尝试将32位图像b8r8g8a8转换为24位图像r8g8b8和16位图像r5g5b5。 但结果很奇怪,也许我不明白如何正确地转换图像。如何正确处理和固定颜色?

    输入图像:

    enter image description here

    Convert32to16() :

    enter image description here

    Convert32to24() :

    enter image description here

    标准传真H

    // stdafx.h : include file for standard system include files,
    // or project specific include files that are used frequently, but
    // are changed infrequently
    //
    
    #pragma once
    
    #include "targetver.h"
    
    #include <stdio.h>
    #include <tchar.h>
    #include <iostream>
    #include <fstream>
    #include <windows.h>
    
    
    // TODO: reference additional headers your program requires here
    

    图像转换器.cpp

    #include "stdafx.h"
    
    
    using std::cout;
    using std::endl;
    using std::ofstream;
    using std::ifstream;
    
    void Convert32to24(void* B8G8R8A8, BYTE* R8G8B8, int width, int height)
    {
        long B8G8R8A8Size = (width * height * 4);
        long j = 0;
        for (long i = 0; i < (B8G8R8A8Size - 3); i = i + 4)
        {
            BYTE Red = ((PBYTE)B8G8R8A8)[i + 2];
            BYTE Green = ((PBYTE)B8G8R8A8)[i + 1];
            BYTE Blue = ((PBYTE)B8G8R8A8)[i];
            BYTE Alpha = ((PBYTE)B8G8R8A8)[i + 3];
    
            R8G8B8[j] = Red;
            R8G8B8[j + 1] = Green;
            R8G8B8[j + 2] = Blue;
            j = j + 3;
        }
    }
    
    void Convert32to16(void* B8G8R8A8, BYTE* R5G5B5, int width, int height)
    {
        long B8G8R8A8Size = (width * height * 4);
    
        long j = 0;
        for (long i = 0; i < (B8G8R8A8Size - 3); i = i + 4)
        {
            BYTE Red = ((PBYTE)B8G8R8A8)[i + 2] >> 3;
            BYTE Green = ((PBYTE)B8G8R8A8)[i + 1] >> 3;
            BYTE Blue = ((PBYTE)B8G8R8A8)[i] >> 3;
            BYTE Alpha = ((PBYTE)B8G8R8A8)[i + 3];
    
            uint16_t RGB565 = ((Red >> 3) << 11) | ((Green >> 2) << 5) | (Blue >> 3);
    
            R5G5B5[j] = RGB565 >> 8;
            R5G5B5[j + 1] = RGB565 & 0xFF;
            j = j + 2;
        }
    }
    
    void WriteDataToBmp(const WCHAR *filename, void *imageData, int width, int height, int BitCount, int bytesPerPixel)
    {
        HANDLE hdl = INVALID_HANDLE_VALUE;
        DWORD bytesWritten;
        BITMAPFILEHEADER fileHeader;
        BITMAPINFOHEADER fileInfo;
    
        fileInfo.biSize = sizeof(BITMAPINFOHEADER);
        fileInfo.biBitCount = BitCount;
        fileInfo.biCompression = BI_RGB;
        fileInfo.biWidth = width;
        fileInfo.biHeight = 0 - height;
        fileInfo.biPlanes = 1;
        fileInfo.biSizeImage = (width * height * bytesPerPixel);
    
        fileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + fileInfo.biSizeImage;
        fileHeader.bfType = 'MB';
        fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    
        hdl = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
    
        if (hdl == INVALID_HANDLE_VALUE)
        {
            return;
        }
    
        WriteFile(hdl, &fileHeader, sizeof(fileHeader), &bytesWritten, NULL);
        WriteFile(hdl, &fileInfo, sizeof(fileInfo), &bytesWritten, NULL);
        WriteFile(hdl, imageData, fileInfo.biSizeImage, &bytesWritten, NULL);
    
        CloseHandle(hdl);
    }
    
    unsigned char* ReadDataFromBmp(char* filename)
    {
        FILE* f = fopen(filename, "rb");
        unsigned char info[54];
        fread(info, sizeof(unsigned char), 54, f);
        int width = *(int*)&info[18];
        int height = *(int*)&info[22];
    
        int size = abs(4 * width * height);
        unsigned char* data = new unsigned char[size];
        fread(data, sizeof(unsigned char), size, f);
        fclose(f);
        return data;
    }
    
    int main(int args, char** cat) {
    
        int width = 1440;
        int height = 900;
    
        int bytesOnPixel;
        BYTE *OutputImage24Bit = new BYTE[width * height * 3];
        BYTE *OutputImage16Bit = new BYTE[width * height * 2];
        unsigned char* inputImage32Bit = ReadDataFromBmp((char*)"E:/TestImage.bmp");
    
        bytesOnPixel = 2;
        Convert32to16(inputImage32Bit, OutputImage16Bit, width, height);
        WriteDataToBmp(L"E:/TestImage16bit.bmp", OutputImage16Bit, width, height, 8 * bytesOnPixel, bytesOnPixel);
    
        bytesOnPixel = 3;
        Convert32to24(inputImage32Bit, OutputImage24Bit, width, height);
        WriteDataToBmp(L"E:/TestImage24bit.bmp", OutputImage24Bit, width, height, 8 * bytesOnPixel, bytesOnPixel);
        return 1;
    }
    
    1 回复  |  直到 8 年前
        1
  •  1
  •   Barmak Shemirani    8 年前

    fileInfo.biCompression = BI_RGB;

    16位位图使用 BI_BITFIELDS 压缩。此外,16位位图必须填充颜色表,以显示它是使用555格式、565格式还是其他格式。

    24位和16位位图需要填充。尽管如此,如果以字节为单位的宽度是4的倍数,这不是问题。一般来说,您不能一个像素接一个像素地读/写,因为填充会将所有内容丢弃。相反,做两个环来穿过高度和宽度。像素大小也取决于填充。

    请注意,您可以对或gdi+或wic执行相同的操作。可以将位图更改为不同的格式 PixelFormat16bppRGB555, PixelFormat16bppRGB565, PixelFormat16bppARGB1555, PixelFormat24bppRGB...

    GDI+示例:

    int main()
    {
        Gdiplus::GdiplusStartupInput tmp;
        ULONG_PTR token;
        Gdiplus::GdiplusStartup(&token, &tmp, NULL);
    
        auto *source = Gdiplus::Bitmap::FromFile(L"test.bmp");
        auto *destination = source->Clone(0, 0, source->GetWidth(), source->GetHeight(),
            PixelFormat16bppRGB565);
    
        CLSID clsid_bmp;
        CLSIDFromString(L"{557cf400-1a04-11d3-9a73-0000f81ef32e}", &clsid_bmp);
        destination->Save(L"copy.bmp", &clsid_bmp);
        delete destination;
        delete source;
    
        Gdiplus::GdiplusShutdown(token);
        return 0;
    }
    

    自制版本:(使用 std::vector 为了记忆,而不是 new/delete )

    void Convert32to24(const wchar_t* file, std::vector<BYTE> &src, int width, int height)
    {
        int width_in_bytes_32 = width * 4;
        int width_in_bytes_24 = ((width * 24 + 31) / 32) * 4;
        DWORD size = width_in_bytes_24 * height;
        std::vector<BYTE> dst(size);
    
        for(int h = 0; h < height; h++)
            for(int w = 0; w < width; w++)
            {
                int i = h * width_in_bytes_32 + w * 4;
                int j = h * width_in_bytes_24 + w * 3;
                dst[j + 0] = src[i + 0];
                dst[j + 1] = src[i + 1];
                dst[j + 2] = src[i + 2];
            }
    
        BITMAPFILEHEADER bf = { 'MB', 54 + size, 0, 0, 54 };
        BITMAPINFOHEADER bi = { sizeof(bi), width, height, 1, 24, BI_RGB };
        std::ofstream fout(file, std::ios::binary);
        fout.write((char*)&bf, sizeof(bf));
        fout.write((char*)&bi, sizeof(bi));
        fout.write((char*)&dst[0], size);
    }
    
    void Convert32to16(const wchar_t* file, std::vector<BYTE> &src, int width, int height)
    {
        int width_in_bytes_32 = width * 4;
        int width_in_bytes_16 = ((width * 16 + 31) / 32) * 4;
        DWORD size = width_in_bytes_16 * height;
        std::vector<BYTE> dst(size);
    
        for(int h = 0; h < height; h++)
            for(int w = 0; w < width; w++)
            {
                int i = h * width_in_bytes_32 + w * 4;
                int j = h * width_in_bytes_16 + w * 2;
    
                //555 format, each color is from 0 to 32, instead of 0 to 256
                uint16_t blu = (uint16_t)(src[i + 0] * 31.f / 255.f);
                uint16_t grn = (uint16_t)(src[i + 1] * 31.f / 255.f);
                uint16_t red = (uint16_t)(src[i + 2] * 31.f / 255.f);
                uint16_t sum = (red) | (grn << 5) | (blu << 10);
                memcpy(&dst[j], &sum, 2);
            }
    
        BITMAPFILEHEADER bf = { 'MB', 54 + size, 0, 0, 54 };
        BITMAPINFOHEADER bi = { sizeof(bi), width, height, 1, 16, BI_BITFIELDS };
        std::ofstream fout(file, std::ios::binary);
        fout.write((char*)&bf, sizeof(bf));
        fout.write((char*)&bi, sizeof(bi));
    
        //555 format
        COLORREF color[]{
            0b0000000000011111,//31
            0b0000001111100000,//31 << 5
            0b0111110000000000 //31 << 10
        };
        fout.write((char*)&color, sizeof(color));
        fout.write((char*)&dst[0], size);
    }
    
    
    int main() 
    {
        const wchar_t* file_32 = L"E:\\TestImage.bmp";
        const wchar_t* file_16 = L"E:\\OutputImage16Bit.bmp";
        const wchar_t* file_24 = L"E:\\OutputImage24Bit.bmp";
    
        BITMAPFILEHEADER bh;
        BITMAPINFOHEADER bi;
        std::ifstream fin(file_32, std::ios::binary);
        if(!fin)
            return 0;
        fin.read((char*)&bh, sizeof(bh));
        fin.read((char*)&bi, sizeof(bi));
        if(bi.biBitCount != 32)
            return 0;
        std::vector<BYTE> source(bh.bfSize);
        fin.read((char*)&source[0], bh.bfSize);
        Convert32to16(file_16, source, bi.biWidth, bi.biHeight);
        Convert32to24(file_24, source, bi.biWidth, bi.biHeight);
        return 0;
    }