首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++将32位bmp图像转换为24位bmp和16位bmp

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

Stack Overflow用户
提问于 2018-07-19 19:41:43
回答 1查看 2.9K关注 0票数 2

尝试将32位图像B8R8G8A8转换为24位图像R8G8B8和16位R5G5B5。但是结果是很奇怪的,也许我不知道如何正确地转换图像。如何正确地处理和修复颜色?

输入图像:

Convert32to16()

Convert32to24()

stdafx.h

代码语言:javascript
复制
// 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

ImageConverter.cpp

代码语言:javascript
复制
#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;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-07-19 20:58:26

fileInfo.biCompression = BI_RGB;

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

24位和16位位图需要填充.尽管如此,如果以字节为单位的宽度是4的倍数,则这不是一个问题。通常,您不能逐个像素地读/写像素,因为填充可以抛出所有的内容。相反,让2圈通过高度和宽度。像素大小也将取决于填充。

请注意,您可以对GDI+或WIC进行同样的操作。您可以将位图更改为不同格式的PixelFormat16bppRGB555, PixelFormat16bppRGB565, PixelFormat16bppARGB1555, PixelFormat24bppRGB...

GDI+示例:

代码语言:javascript
复制
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)

代码语言:javascript
复制
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;
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51430619

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档