Search code examples
c++image-processingwindows-cebmp

How to create a .bmp in WinCE


I have a camera that returns raw images that can easily be converted to a bitmap that can be saved to a file by the following C# method (that I did not write). From various sources, I have determined that the pictures have 8 bits per pixel, and may or may not be grayscale.

private void rawImgToBmp(byte[] imgData, String fname) {
        Bitmap bmp = new Bitmap(getWidth(), getHeight(), 
            System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
        for (int i = 0; i < 256; i++)
            { bmp.Palette.Entries[i] = Color.FromArgb(255, i, i, i); }
        //Copy the data from the byte array into the bitmap
        BitmapData bmpData = 
            bmp.LockBits( new Rectangle(0, 0, bmp.Width, bmp.Height),
                          ImageLockMode.WriteOnly, bmp.PixelFormat);
        Marshal.Copy(imgData, 0, bmpData.Scan0, getWidth() * getHeight());
        bmp.UnlockBits(bmpData); //Unlock the pixels
        bmp.Save(FileName);
    }

My question is: how would I go about writing the equivalent method in C++, using built in functions of Windows CE 4.2?

erisu: thanks for the palette code, I think it's right. I've resorted to filling in the rest of the structs manually, according to the Wikipedia page.


Solution

  • This is the code that works for me. It is based on erisu's answer and Wikipedia's description of the BMP format. For anyone else using this answer, I recommend that you understand the BMP format as fully as possible, so you can adjust the header fields accordingly.

    The complicated loop at the end is my workaround for an issue with my hardware/OS, where it would not write all of the data I supplied to fwrite. It should work in any environment, though.

    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000
    
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <tchar.h>
    
    #define NPAL_ENT 256
    
    INT WINAPI WinMain( HINSTANCE hInstance,
                        HINSTANCE hPrevInstance,
                        LPTSTR lpCmdLine,
                        INT nShowCmd )
    {
        int w = 1920, h = 1080; // My values, yours may vary
    
    //////////////////////// File Operations ///////////////////////////////
    
        // Reading raw img
        FILE* f = fopen("\\FlashDisk\\raw_img.bin","r");
        if(NULL == f){printf("BAD");exit(1);}
    
        // Obtaining size of raw img
        fseek (f , 0L , SEEK_END);
        DWORD fsize = (DWORD)ftell (f);
        fseek (f , 0L , SEEK_SET);
    
        char *imgData = (char*) malloc (sizeof(char)*fsize);
        if(NULL == imgData) {printf("NOT imgData");exit(2);}
    
        // Copy contents of file into buffer
        DWORD result = fread(imgData,1,fsize,f);
        if (result != fsize) {
            printf ("Reading error. Expected: %d, Got: %d\n",fsize, result ); 
            if(ferror(f)){printf("An error: %d\n", ferror(f)); }
            if(feof(f)) {printf("EOF\n");}
            delete[] imgData;
            fclose(f);
            exit (3);
        }
        fclose(f);
    
    //////////////////////// BMP Operations ///////////////////////////////
    
        /* A bitmap has the following components:
         *  1. BMP file header
         *  2. Bitmap Information (DIB) header
         *  3. Color Palette
         *  4. Raw Data
         */
        BITMAPFILEHEADER bmfh;
        ZeroMemory( &bmfh, sizeof( bmfh ) );
        bmfh.bfType = 0x4D42; // Magic #
        bmfh.bfSize = sizeof( bmfh ) + sizeof( BITMAPINFOHEADER )
            + NPAL_ENT*sizeof(PALETTEENTRY) + w*h; // Or total file size if w/h not known
        bmfh.bfOffBits = sizeof( bmfh ) + sizeof( BITMAPINFOHEADER )
            + NPAL_ENT*sizeof(PALETTEENTRY);
    
        BITMAPINFOHEADER bmih;
        ZeroMemory( &bmih, sizeof( bmih ) );
        bmih.biWidth = w;
        bmih.biHeight = h;
        bmih.biSize = sizeof(bmih);
        bmih.biPlanes = 1;
        bmih.biBitCount = 8;
        bmih.biCompression = BI_RGB;
        bmih.biSizeImage = w * h;
    
        int palSize = NPAL_ENT*sizeof(PALETTEENTRY);
        LOGPALETTE *logpal=(LOGPALETTE*)new BYTE[sizeof(LOGPALETTE)+palSize];
        if(!logpal) {delete [] imgData; printf("!logpal\n"); exit(4);}
        logpal->palVersion=0x300;
        logpal->palNumEntries=NPAL_ENT;
        int i=0;
        do {  // Exact palette format varies. This is what worked for me
            logpal->palPalEntry[i].peRed=i;
            logpal->palPalEntry[i].peGreen=i;
            logpal->palPalEntry[i].peBlue=i;
            logpal->palPalEntry[i].peFlags=NULL;
        } while(++i<NPAL_ENT);
    
        // Complete bitmap is now in memory, time to save it
        TCHAR bmpfname[80];
        wsprintf( bmpfname, (TCHAR*) TEXT( "\\USBDisk\\out.bmp" ) );
    
        // open the file for writing
        FILE *bmpFile = _wfopen(bmpfname,L"wb"); 
        if(!bmpFile) { delete[] imgData; delete[] logpal; exit(6); }
    
        // write the bitmap to file, in whatever chunks WinCE allows
        size_t totWrit = 0, offset = 0, writeAmt = 0;
        while(totWrit < bmfh.bfSize){
            if(totWrit < sizeof(bmfh)){ // File header
                offset = totWrit;
                totWrit += fwrite( ((char*)&bmfh)+offset, 1, sizeof(bmfh)-offset, bmpFile );
            }
            else if(totWrit<sizeof(bmfh)+sizeof(bmih)){ // Image header
                offset = totWrit - sizeof(bmfh);
                totWrit += fwrite( ((char*)&bmih)+offset, 1, sizeof(bmih)-offset, bmpFile );
            }
            else if(totWrit<sizeof(bmfh)+sizeof(bmih)+palSize) { // Pallette
                offset = totWrit - sizeof(bmfh) - sizeof(bmih);
                totWrit += fwrite( ((char*)&logpal->palPalEntry)+offset, 1, palSize-offset, bmpFile );
            }
            else { // Image data
                offset = totWrit - sizeof(bmfh) - sizeof(bmih) - palSize;
                if(bmfh.bfSize-totWrit >= IO_SIZE) {
                    writeAmt = IO_SIZE;
                }
                else {
                    writeAmt = bmfh.bfSize-totWrit;
                }
                totWrit += fwrite( &imageBuffer[offset], 1, writeAmt, bmpFile );
            }
    
            // Close and open after each iteration to please WinCE
            fflush(bmpFile);
            fclose(bmpFile);
            Sleep(4000); 
            bmpFile = _wfopen(bmpfname,L"ab");
            if(!bmpFile) {flog->lprintf("Couldn't reopen bmpfile"); delete [] logpal; return 0;}
        }
        fclose(bmpFile);
    
        if(totWrit != bmfh.bfSize) {
            printf("BMP Size mismatch: %d/%d.",totWrit,bmfh.bfSize);
            delete [] imgData;
            delete [] logpal;
            exit(-1);
        }
       // Cleanup
        delete [] imgData;
        delete [] logpal;
    
        return 0;
    
    }