C Win32: сохранить изображение .bmp с HBITMAP

Я работаю с framegrabber и должен получить изображения из памяти компьютера и сохранить их в файле изображения. после попытки в течение нескольких дней я получаю следующие 2 функции, которые создают файл, а ОС Windows может запускать файл .bmp, но растровый файл является черным (размер изображения составляет 900 КБ, 640 * 480). у любого органа есть идея, почему, картина в черном? вот две функции:

LPSTR CreateBMP( HWND hAppWnd, int nImageType )
{          
    void            * pWinGBits = NULL;
    int             i;
    Z_BITMAPINFO    zWinGHeader;    //  bitmapinfo for cerating the DIB
    //  create DC for bitmap.
    hDCBits = CreateCompatibleDC( ghDCMain );

    switch ( nImageType )
    {
        case bayer_filter:
        zWinGHeader.bmiHeader.biSize            = sizeof( BITMAPINFOHEADER );
        zWinGHeader.bmiHeader.biPlanes          = 1;
        zWinGHeader.bmiHeader.biClrImportant    = 0;
        zWinGHeader.bmiHeader.biHeight          = -lYSize;
        zWinGHeader.bmiHeader.biWidth           = lXSize;
        zWinGHeader.bmiHeader.biBitCount        = 32;
        zWinGHeader.bmiHeader.biClrUsed         = 0;//3;
        zWinGHeader.bmiHeader.biCompression     = BI_BITFIELDS;
        zWinGHeader.bmiHeader.biSizeImage       = 0;
        zWinGHeader.bmiColors[0].rgbBlue        = 0x00;
        zWinGHeader.bmiColors[0].rgbGreen       = 0x00;
        zWinGHeader.bmiColors[0].rgbRed         = 0xFF;
        zWinGHeader.bmiColors[0].rgbReserved    = 0x00;

        zWinGHeader.bmiColors[1].rgbBlue        = 0x00;
        zWinGHeader.bmiColors[1].rgbGreen       = 0xFF;
        zWinGHeader.bmiColors[1].rgbRed         = 0x00;
        zWinGHeader.bmiColors[1].rgbReserved    = 0x00;

        zWinGHeader.bmiColors[2].rgbBlue        = 0xFF;
        zWinGHeader.bmiColors[2].rgbGreen       = 0x00;
        zWinGHeader.bmiColors[2].rgbRed         = 0x00;
        zWinGHeader.bmiColors[2].rgbReserved    = 0x00;
        break;

        case color32:
        zWinGHeader.bmiHeader.biSize            = sizeof( BITMAPINFOHEADER );
        zWinGHeader.bmiHeader.biPlanes          = 1;
        zWinGHeader.bmiHeader.biClrImportant    = 0;
        zWinGHeader.bmiHeader.biHeight          = -lYSize;
        zWinGHeader.bmiHeader.biWidth           = lXSize/4;
        zWinGHeader.bmiHeader.biBitCount        = 32;
        zWinGHeader.bmiHeader.biClrUsed         = 0;
        zWinGHeader.bmiHeader.biCompression     = BI_BITFIELDS;
        zWinGHeader.bmiHeader.biSizeImage       = 0;
        zWinGHeader.bmiColors[0].rgbBlue        = 0x00;
        zWinGHeader.bmiColors[0].rgbGreen       = 0x00;
        zWinGHeader.bmiColors[0].rgbRed         = 0xFF;
        zWinGHeader.bmiColors[0].rgbReserved    = 0x00;

        zWinGHeader.bmiColors[1].rgbBlue        = 0x00;
        zWinGHeader.bmiColors[1].rgbGreen       = 0xFF;
        zWinGHeader.bmiColors[1].rgbRed         = 0x00;
        zWinGHeader.bmiColors[1].rgbReserved    = 0x00;

        zWinGHeader.bmiColors[2].rgbBlue        = 0xFF;
        zWinGHeader.bmiColors[2].rgbGreen       = 0x00;
        zWinGHeader.bmiColors[2].rgbRed         = 0x00;
        zWinGHeader.bmiColors[2].rgbReserved    = 0x00;
        break;

        case color24:
        zWinGHeader.bmiHeader.biSize            = sizeof( BITMAPINFOHEADER );
        zWinGHeader.bmiHeader.biPlanes          = 1;
        zWinGHeader.bmiHeader.biClrImportant    = 0;
        zWinGHeader.bmiHeader.biHeight          = -lYSize;
        zWinGHeader.bmiHeader.biWidth           = lXSize/3;
        zWinGHeader.bmiHeader.biBitCount        = 24;
        zWinGHeader.bmiHeader.biClrUsed         = 0;
        zWinGHeader.bmiHeader.biCompression     = BI_RGB;
        zWinGHeader.bmiHeader.biSizeImage       = 0;
        break;

        case color3x16:
        zWinGHeader.bmiHeader.biSize            = sizeof( BITMAPINFOHEADER );
        zWinGHeader.bmiHeader.biPlanes          = 1;
        zWinGHeader.bmiHeader.biClrImportant    = 0;
        zWinGHeader.bmiHeader.biHeight          = -lYSize;
        zWinGHeader.bmiHeader.biWidth           = lXSize/6;
        zWinGHeader.bmiHeader.biBitCount        = 32;
        zWinGHeader.bmiHeader.biClrUsed         = 0;
        zWinGHeader.bmiHeader.biCompression     = BI_BITFIELDS;
        zWinGHeader.bmiHeader.biSizeImage       = 0;
        zWinGHeader.bmiColors[0].rgbBlue        = 0x00;
        zWinGHeader.bmiColors[0].rgbGreen       = 0x00;
        zWinGHeader.bmiColors[0].rgbRed         = 0xFF;
        zWinGHeader.bmiColors[0].rgbReserved    = 0x00;

        zWinGHeader.bmiColors[1].rgbBlue        = 0x00;
        zWinGHeader.bmiColors[1].rgbGreen       = 0xFF;
        zWinGHeader.bmiColors[1].rgbRed         = 0x00;
        zWinGHeader.bmiColors[1].rgbReserved    = 0x00;

        zWinGHeader.bmiColors[2].rgbBlue        = 0xFF;
        zWinGHeader.bmiColors[2].rgbGreen       = 0x00;
        zWinGHeader.bmiColors[2].rgbRed         = 0x00;
        zWinGHeader.bmiColors[2].rgbReserved    = 0x00;
        break;

        case bw1x10:
        //  create bitmap-infoheader.
        zWinGHeader.bmiHeader.biSize        = sizeof( BITMAPINFOHEADER );
        zWinGHeader.bmiHeader.biPlanes      = 1;
        zWinGHeader.bmiHeader.biBitCount    = 8;
        zWinGHeader.bmiHeader.biCompression = BI_RGB;
        zWinGHeader.bmiHeader.biSizeImage   = 0;
        zWinGHeader.bmiHeader.biClrUsed     = 256;
        zWinGHeader.bmiHeader.biClrImportant= 0;
        zWinGHeader.bmiHeader.biHeight      = -lYSize;
        zWinGHeader.bmiHeader.biWidth       = lXSize/2;

        //  create colortable fot bitmap (grayvalues).
        for (i = 0; i < 256; i++) 
        {
            zWinGHeader.bmiColors[i].rgbGreen   = i;
            zWinGHeader.bmiColors[i].rgbBlue    = i;
            zWinGHeader.bmiColors[i].rgbRed     = i;

            zWinGHeader.bmiColors[i].rgbReserved = 0;
        }
        break;

        default:
        case bw8:
        //  create bitmap-infoheader.
        zWinGHeader.bmiHeader.biSize        = sizeof( BITMAPINFOHEADER );
        zWinGHeader.bmiHeader.biPlanes      = 1;
        zWinGHeader.bmiHeader.biBitCount    = 8;
        zWinGHeader.bmiHeader.biCompression = BI_RGB;
        zWinGHeader.bmiHeader.biSizeImage   = 0;        
        zWinGHeader.bmiHeader.biClrUsed     = (1<<8);       
        zWinGHeader.bmiHeader.biClrImportant= 0;
        zWinGHeader.bmiHeader.biHeight      = -lYSize;
        zWinGHeader.bmiHeader.biWidth       = lXSize;
        //zWinGHeader.bmiHeader.biSizeImage = ((zWinGHeader.bmiHeader.biWidth * 8 +31) & ~31) /8
            //                                  * zWinGHeader.bmiHeader.biHeight; 

        //  create colortable fot bitmap (grayvalues).
        for (i = 0; i < 256; i++) 
        {
            zWinGHeader.bmiColors[i].rgbGreen   = i;        
            zWinGHeader.bmiColors[i].rgbBlue    = i;
            zWinGHeader.bmiColors[i].rgbRed     = i;            
            zWinGHeader.bmiColors[i].rgbReserved = 0;           
        }
        break;
    }

    //  cerate identity palette 
    hPal = CreateIdentityPalette( zWinGHeader.bmiColors );

    //  get new palette into DC and map into physical palette register.
    hOldPal = SelectPalette( ghDCMain, hPal, FALSE);    
    RealizePalette( ghDCMain );

    //  cerate DIB-Section f黵 direct access of image-data.
    hBitmap = CreateDIBSection(
        hDCBits,                        //  handle of device context
        (BITMAPINFO *)&zWinGHeader,     //  address of structure containing
                                        //  bitmap size, format and color data
        DIB_RGB_COLORS,                 //  color data type indicator: RGB values
                                        //  or palette indices
        &pWinGBits,                     //  pointer to variable to receive a pointer
                                        //  to the bitmap bit values
        NULL,                           //  optional handle to a file mapping object
        0                               //  offset to the bitmap bit values within
                                        //  the file mapping object
    );      
    //  get bitmap into DC .
    hOldBitmap = (HBITMAP)SelectObject( hDCBits, hBitmap );

    return pWinGBits;           //  return pointer to DIB 
}

и вот функция сохранения в .bmp:

BOOL SaveToFile(HBITMAP hBitmap3, LPCTSTR lpszFileName)
{   
  HDC hDC;
  int iBits;
  WORD wBitCount;
  DWORD dwPaletteSize=0, dwBmBitsSize=0, dwDIBSize=0, dwWritten=0;
  BITMAP Bitmap0;
  BITMAPFILEHEADER bmfHdr;
  BITMAPINFOHEADER bi;
  LPBITMAPINFOHEADER lpbi;
  HANDLE fh, hDib, hPal,hOldPal2=NULL;
  hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
  DeleteDC(hDC);
  if (iBits <= 1)
    wBitCount = 1;
  else if (iBits <= 4)
    wBitCount = 4;
  else if (iBits <= 8)
    wBitCount = 8;
  else
    wBitCount = 24; 
  GetObject(hBitmap3, sizeof(Bitmap0), (LPSTR)&Bitmap0);
  bi.biSize = sizeof(BITMAPINFOHEADER);
  bi.biWidth = Bitmap0.bmWidth;
  bi.biHeight =-Bitmap0.bmHeight;
  bi.biPlanes = 1;
  bi.biBitCount = wBitCount;
  bi.biCompression = BI_RGB;
  bi.biSizeImage = 0;
  bi.biXPelsPerMeter = 0;
  bi.biYPelsPerMeter = 0;
  bi.biClrImportant = 0;
  bi.biClrUsed = 256;
  dwBmBitsSize = ((Bitmap0.bmWidth * wBitCount +31) & ~31) /8
                                                * Bitmap0.bmHeight; 
  hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));
  lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
  *lpbi = bi;

  hPal = GetStockObject(DEFAULT_PALETTE);
  if (hPal)
  { 
    hDC = GetDC(NULL);
    hOldPal2 = SelectPalette(hDC, (HPALETTE)hPal, FALSE);
    RealizePalette(hDC);
  }


  GetDIBits(hDC, hBitmap3, 0, (UINT) Bitmap0.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER) 
    +dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS);

  if (hOldPal2)
  {
    SelectPalette(hDC, (HPALETTE)hOldPal2, TRUE);
    RealizePalette(hDC);
    ReleaseDC(NULL, hDC);
  }

  fh = CreateFile(lpszFileName, GENERIC_WRITE,0, NULL, CREATE_ALWAYS, 
    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); 

  if (fh == INVALID_HANDLE_VALUE)
    return FALSE; 

  bmfHdr.bfType = 0x4D42; // "BM"
  dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;
  bmfHdr.bfSize = dwDIBSize;
  bmfHdr.bfReserved1 = 0;
  bmfHdr.bfReserved2 = 0;
  bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;

  WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);

  WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);
  GlobalUnlock(hDib);
  GlobalFree(hDib);
  CloseHandle(fh);
  counter=1;
  return TRUE;
} 

Я МОГУ ЧЕРТЕЖИТЬ ИЗОБРАЖЕНИЙ из памяти с помощью следующей функции, поэтому, конечно, у меня нет проблем для чтения данных изображения:

void DrawPicture( HDC hDC, PBYTE pDest, PBYTE pSrc, LONG lXSize, LONG lYSize )
{    
LONG    lXSizeDiv, lYSizeDiv;
LONG    lX, lY;
DWORD   dwMax;
RECT    rect;
HDC     hdc;
PBYTE   pTmpDest;
double  fXFactor, fYFactor;
//  HBRUSH  hBrush;
//  POINT   Point;


switch( gzMvfgKamDef.iImageType )
{
    case bayer_filter:
    lXSizeDiv = 1;
    lYSizeDiv = 1;
    BayerToRGB( (PDWORD) pDest, pSrc, lXSize, lYSize, 
                gzMvfgKamDef.iBayerQuadrant, gzMvfgKamDef.iBayerQuality );

    break;

    case color24:
    lXSizeDiv = 3;
    lYSizeDiv = 1;
    memcpy( pDest, pSrc, (size_t)( lXSize * lYSize ) );

break;

    case color32:
    lXSizeDiv = 4;
    lYSizeDiv = 1;
    memcpy( pDest, pSrc, (size_t)( lXSize * lYSize ) );


    break;

    case color3x16:
    lXSizeDiv = 6;
    lYSizeDiv = 1;
    Conv3x16To3x8( pDest, pSrc, 4, lXSize, lYSize );


    break;

    case bw1x10:
    lXSizeDiv = 2;
    lYSizeDiv = 1;
    Conv1x10To1x8( pDest, pSrc, 2, lXSize, lYSize );


    break;

    case bw6Tap:
    lXSizeDiv = 1;
    lYSizeDiv = 1;
    Conv6TapTo1x8( pDest, pSrc, 1, lXSize, lYSize );

    break;

    default:
    case bw8:
    lXSizeDiv = 1;
    lYSizeDiv = 1;
    memcpy( pDest, pSrc, (size_t)( lXSize * lYSize ) );

    break;

}


if( gahIniDlg[ NdxHistogramDlg ].hWnd )
{
    memset( gadwHistogram, 0, sizeof( gadwHistogram) );

    pTmpDest = pDest;

    for ( lY = 0; lY < lYSize; lY++ )
    {
        for ( lX = 0; lX < lXSize; lX++ )
        {
            gadwHistogram[ *pTmpDest ]++;
            pTmpDest++;
        }
    }

    GetClientRect ( gahIniDlg[ NdxHistogramDlg ].hWnd, &rect) ;

    hdc = GetDC ( gahIniDlg[ NdxHistogramDlg ].hWnd );

    dwMax = 0;
    for ( lX = 0 ; lX <= 0xff; lX++ )
        dwMax = ( gadwHistogram[ lX ] > dwMax ) ? gadwHistogram[ lX ] : dwMax;

    fYFactor = (double) dwMax / (double) rect.bottom;
    fXFactor = (double) ( rect.right - 100 ) / (double) 0x100;

/*
    SelectObject (hdc, GetStockObject (BLACK_PEN)) ;

    for( lX = 0; lX <= 0xff; lX+=8 )
    {
        MoveToEx( hdc, lX * fXFactor, rect.bottom, &Point );
        LineTo( hdc, lX * fXFactor, rect.bottom-10 );
    }
*/
    SelectObject (hdc, GetStockObject (WHITE_PEN)) ;

//      hBrush = CreateSolidBrush( GetSysColor( COLOR_WINDOW ));
//      hBrush = CreateSolidBrush( 0xffffff );
//      SelectObject (hdc, hBrush);


    Polyline( hdc, gaHistogram, 0x100 );

//      DeleteObject( hBrush );

    for ( lX = 0 ; lX <= 0xff; lX++ )
    {
        gaHistogram[ lX ].x = (DWORD)( fXFactor * (double)lX ); 
        gaHistogram[ lX ].y = rect.bottom - (DWORD)( (double) gadwHistogram[ lX ] / fYFactor );
    }

    SelectObject (hdc, GetStockObject (BLACK_PEN)) ;

    Polyline( hdc, gaHistogram, 0x100 );

    ReleaseDC ( gahIniDlg[ NdxHistogramDlg ].hWnd , hdc ) ;
}

//  display the bitmap
StretchBlt( hDC, rcWin.left, rcWin.top, lXSize / lXSizeDiv, lYSize / lYSizeDiv, 
        hDCBits, 0, 0, lXSize / lXSizeDiv, lYSize / lYSizeDiv, SRCCOPY );
//BitBlt( hDC, rcWin.left, rcWin.top, lXSize / lXSizeDiv, lYSize / lYSizeDiv, 
            //hDCBits, 0, 0,  SRCCOPY );

}

Ответ 1

Я просто узнал, что у меня была глупая ошибка в другой части моего кода, который я использовал:

// Convert Image to bitmap and display it
DrawPicture( ghDCMain, pWinGBitmap, gpbImageData, lXSize, lYSize  );    
if(counter!=1) {
  hBitmap2 = CreateCompatibleBitmap (hDCBits, lXSize, lYSize);
  SaveToFile(hBitmap2, "c:\\t.bmp");
  OutputDebugString("tested !!!!");
}

удалив строку hBitmap2 = CreateCompatibleBitmap (hDCBits, lXSize, lYSize); и изменив hBitmap2 на hBitmap результат CreateDIBSection(), он сохранил изображение красиво. СПАСИБО ВАМ ВСЕ ДЛЯ ВАШЕЙ ПОМОЩИ.

Ответ 2

FORMAT_INFO sFormat;
mvfg_getparam( MVFGPAR_DATAFORMAT, &sFormat, giCurrentGrabberID);
long lFrameSize     = sFormat.lFrameSize;

BYTE *pBitmap = (BYTE*)malloc(FrameSize);
memcpy( pBitmap, CamGetPicture(), FrameSize );
writeBitmapToFile(pBitmap, FrameSize, "tmp.bmp");

/*
 * write our raw data into a bitmap file
 * adding the correct header and put it all
 * together in a single file
 */
int
writeBitmapToFile(void *data, unsigned long size, const char *filename)
{
    bmpFileHeader_t header;
    bmpInfoHeader_t info;
    bmpRGBQuad_t rgb = { 0, 0, 0, 0};
    FILE *out;
    size_t len;
    int i;


    header.type = 0x4d42; // magic sequence 'BM'
    header.size = 0;
    header.res0 = 0;
    header.res1 = 0;
    /* 256 different colors, each is defined by an bmpRBQuad type */
    header.offset = 256 * sizeof(bmpRGBQuad_t) + 
                    sizeof(bmpInfoHeader_t) + sizeof(bmpFileHeader_t);
    info.size = sizeof(bmpInfoHeader_t);
    info.width = WIDTH;
    info.height = HEIGHT;
    info.planes = 1;
    info.cDepth = 8; /* 8 Bit */
    info.compression = 0;
    info.rawSize = size;
    info.hRes = 0;
    info.vRes = 0;
    info.nrColors = 0x100;
    info.impColors = 0;

    out = fopen(filename, "wb");
    if (out == NULL) {
        printf("error cannot open %s for writing\n", filename);
        return -1;
    }
    len = fwrite(&header, 1, sizeof(header), out);
    if (len != sizeof(header)) {
        printf("error while writing header\n");
        return -1;
    }
    len = fwrite(&info, 1, sizeof(info), out);
    if (len != sizeof(info)) {
        printf("error while writing info header\n");
        return -1;
    }
    /* stupid try and error programming leads to this */
    for (i = 0; i < 256; i++) {
        rgb.red = i;
        rgb.green = i;
        rgb.blue = i;
        len = fwrite(&rgb, 1, sizeof(rgb), out);
        if (len != sizeof(rgb)) {
            printf("error while writing rgb header\n");
            return -1;
        }
    }

    len = fwrite(data, 1, size, out);
    if (len != size ) {
        printf("error while writing bitmap data\n");
        return -1;
    }
    return 0;
}

Ссылка - микротрон

Ссылка - запись растрового изображения

Ответ 3

Вы уверены, что там есть правильные данные?

Я попробую извлечь данные RGB и сохранить его в очень простом формате "вручную", просто чтобы увидеть, что там находится. Если вы ищете мертвый простой, но все же стандартный формат изображения, просмотрите изображения PPM.

Ответ 4

Из документации для CreateCompatibleDC:

Когда создается DC памяти, его поверхность дисплея имеет ровно один монохромный пиксель в ширину и один монохромный пиксель. Прежде чем приложение сможет использовать DC для памяти для операций рисования, он должен выбрать растровое изображение правильной ширины и высоты в DC.

Поскольку вы используете CreateDIBSection с этим DC, результирующий раздел DIB будет также 1-битным монохромным.