从Byte数组创建CImage

时间:2011-07-14 09:47:12

标签: c++ mfc atl

我需要从一个字节数组创建一个CImage(实际上,它是一个unsigned char数组,但我可以转换为必要的任何形式)。字节数组的格式为“RGBRGBRGB ...”。新图像需要包含图像字节的副本,而不是使用字节数组本身的内存。

我已经尝试了很多不同的方法来实现这一目标 - 包括通过各种HBITMAP创建函数,尝试使用BitBlt - 到目前为止还没有任何工作。

要测试该功能是否有效,它应通过此测试:

BYTE* imgBits;
int width;
int height;
int Bpp;   // BYTES per pixel (e.g. 3)
getImage(&imgBits, &width, &height, &Bpp); // get the image bits

// This is the magic function I need!!!
CImage img = createCImage(imgBits, width, height, Bpp);

// Test the image
BYTE* data = img.GetBits();  // data should now have the same data as imgBits

到目前为止,createCImage()的所有实现都以[{1}}指向空(零填充)数组结束。

4 个答案:

答案 0 :(得分:1)

CImage非常巧妙地支持DIB,并且有一个SetPixel()方法,所以你可能会做这样的事情(未编译的,未经测试的代码!):

CImage img;
img.Create(width, height, 24 /* bpp */, 0 /* No alpha channel */);

int nPixel = 0;
for(int row = 0; row < height; row++)
{
    for(int col = 0; col < width; col++)
    {
        BYTE r = imgBits[nPixel++];
        BYTE g = imgBits[nPixel++];
        BYTE b = imgBits[nPixel++];
        img.SetPixel(row, col, RGB(r, g, b));
    }
}

也许不是最有效的方法,但我认为这是最简单的方法。

答案 1 :(得分:1)

使用memcpy复制数据,然后使用SetDIBits或SetDIBitsToDevice,具体取决于您需要执行的操作。但请注意,原始图像数据的扫描线在4字节边界上对齐(IIRC,这是我做了几年后的几年)所以从GetDIBits返回的数据永远不会与原始数据完全相同(它可能,取决于图像大小)。

因此,您很可能需要通过扫描线记忆扫描线。

答案 2 :(得分:1)

谢谢大家,我设法在你的帮助下最终解决了这个问题。它主要涉及@tinman和@ Roel的使用SetDIBitsToDevice()的建议,但它涉及一些额外的比特和内存管理,所以我想我在这里分享我的终点。

在下面的代码中,我假设设置了widthheightBpp(每个像素的字节),data是指向RGB像素值数组的指针。

// Create the header info
bmInfohdr.biSize = sizeof(BITMAPINFOHEADER);
bmInfohdr.biWidth = width;
bmInfohdr.biHeight = -height;
bmInfohdr.biPlanes = 1;
bmInfohdr.biBitCount = Bpp*8;
bmInfohdr.biCompression = BI_RGB;
bmInfohdr.biSizeImage = width*height*Bpp;
bmInfohdr.biXPelsPerMeter = 0;
bmInfohdr.biYPelsPerMeter = 0;
bmInfohdr.biClrUsed = 0;
bmInfohdr.biClrImportant = 0;

BITMAPINFO bmInfo;
bmInfo.bmiHeader = bmInfohdr;
bmInfo.bmiColors[0].rgbBlue=255;

// Allocate some memory and some pointers
unsigned char * p24Img = new unsigned char[width*height*3];
BYTE *pTemp,*ptr;
pTemp=(BYTE*)data;
ptr=p24Img;

// Convert image from RGB to BGR
for (DWORD index = 0; index < width*height ; index++)
{
    unsigned char r = *(pTemp++);
    unsigned char g = *(pTemp++);
    unsigned char b = *(pTemp++);   

    *(ptr++) = b;
    *(ptr++) = g;
    *(ptr++) = r;
}

// Create the CImage
CImage im;
im.Create(width, height, 24, NULL);

HDC dc = im.GetDC();
SetDIBitsToDevice(dc, 0,0,width,height,0,0, 0, height, p24Img, &bmInfo, DIB_RGB_COLORS);
im.ReleaseDC();

delete[] p24Img;

答案 3 :(得分:1)

这是一个更简单的解决方案。您可以使用GetPixelAddress(...)代替所有这些BITMAPHEADERINFO和SedDIBitsToDevice。我解决的另一个问题是8位图像,需要定义颜色表。

CImage outImage;
outImage.Create(width, height, channelCount * 8);

int lineSize = width * channelCount;
if (channelCount == 1)
{
    // Define the color table
    RGBQUAD* tab = new RGBQUAD[256];
    for (int i = 0; i < 256; ++i)
    {
        tab[i].rgbRed = i;
        tab[i].rgbGreen = i;
        tab[i].rgbBlue = i;
        tab[i].rgbReserved = 0;
    }
    outImage.SetColorTable(0, 256, tab);
    delete[] tab;
}

// Copy pixel values
// Warining: does not convert from RGB to BGR
for ( int i = 0; i < height; i++ )
{
    void*       dst = outImage.GetPixelAddress(0, i);
    const void* src = /* put the pointer to the i'th source row here */;
    memcpy(dst, src, lineSize);
}