2009-07-22 64 views
6

我需要使用表示單色位圖數據的原始字節創建Bitmap對象。在完整的框架,我做了以下內容:在Compact Framework中創建單色位圖

Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed) 
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat); 
// Write my data into bmpData.Scan0 
bmp.UnlockBits(bmpData); 

不幸的是,Compact Framework不具備PixelFormat.Format8bppIndexed枚舉值。那麼我怎麼才能在CF上完成這個呢?我能想到的唯一辦法是自己手動創建位圖文件頭,並將其與數據一起寫入Stream,然後用該Stream構造一個Bitmap對象。

想法?

回答

0

OpenNetCf智能設備框架庫(有一個免費版本),他們有自己的位圖類(BitmapEx)。雖然我還沒有嘗試過你正在嘗試做什麼,但你可以查看它們的實現。 (注:我必須要做到這一點,在未來一週,所以我可能會更新這個版本)

+0

我看着OpenNETCF,但它依賴於相同的枚舉。看看他們如何實現一些Bitmap類可能會很有用,因爲我非常確定我需要自己手動構建頭部。 :( – Jason 2009-07-27 20:15:47

7

下面是一些代碼,可以幫助:

const int XPelsPerMeter = 0xb12; // 72 ppi, 96 would work well too 
    const int YPelsPerMeter = 0xb12; 
    const int Gptr = 0x40; 
    const int Srccopy = 0x00CC0020; 


    struct BITMAPFILEHEADER 
    { 
    public ushort bfType; 
    public uint bfSize; 
    public ushort bfReserved1; 
    public ushort bfReserved2; 
    public uint bfOffBits; 
    } 

    struct BITMAPINFOHEADER 
    { 
    public uint biSize; 
    public int biWidth; 
    public int biHeight; 
    public ushort biPlanes; 
    public ushort biBitCount; 
    public uint biCompression; 
    public uint biSizeImage; 
    public int biXPelsPerMeter; 
    public int biYPelsPerMeter; 
    public uint biClrUsed; 
    public uint biClrImportant; 
    } 

    public static byte[] GetByteArray(Bitmap bitmap) 
    { 
    IntPtr hbm = bitmap.GetHbitmap(); // this is step (1) 
    IntPtr sdc = GetDC(IntPtr.Zero);  // First we obtain the DC for the screen 
    // Next, create a DC for the original hbitmap 
    IntPtr hdc = CreateCompatibleDC(sdc); 
    SelectObject(hdc, hbm); 

    byte[] arrayBytes = CreateBinary(hdc, bitmap.Height, bitmap.Width); 

    // Finally some cleanup. 
    DeleteDC(hdc); 
    ReleaseDC(IntPtr.Zero, sdc); 
    DeleteObject(hbm); 

    return arrayBytes; 
    } 

    static int WIDTHBYTES(int bits) 
    { 
    return ((((bits) + 31)/32) * 4); 
    } 

    private static byte[] CreateBinary(IntPtr hDc, int height, int width) 
    { 
    IntPtr hMemDc = CreateCompatibleDC(hDc); 

    int cb = 0; 

    BITMAPINFOHEADER bi = new BITMAPINFOHEADER(); 
    bi.biSize = (uint)Marshal.SizeOf(bi); 
    bi.biBitCount = 1; // Creating RGB bitmap. The following three members don't matter 
    bi.biClrUsed = 2; 
    bi.biClrImportant = 2; 
    bi.biCompression = 0; 
    bi.biHeight = height; 
    bi.biWidth = width; 
    bi.biPlanes = 1; 
    cb = WIDTHBYTES(bi.biWidth * bi.biBitCount) * bi.biHeight; 
    bi.biSizeImage = (uint)cb; 
    bi.biXPelsPerMeter = XPelsPerMeter; 
    bi.biYPelsPerMeter = YPelsPerMeter; 

    IntPtr pBits = IntPtr.Zero; 
    //Allocate memory for bitmap bits 
    IntPtr pBi = LocalAlloc(Gptr, bi.biSize); 
    // Not sure if this needed - simply trying to keep marshaller happy 
    Marshal.StructureToPtr(bi, pBi, false); 
    //This will return IntPtr to actual DIB bits in pBits 
    IntPtr hBmp = CreateDIBSection(hDc, pBi, 0, ref pBits, IntPtr.Zero, 0); 
    //Marshall back - now we have BITMAPINFOHEADER correctly filled in 
    //Marshal.PtrToStructure(pBI, bi); 
    BITMAPINFOHEADER biNew = (BITMAPINFOHEADER)Marshal.PtrToStructure(pBi, typeof(BITMAPINFOHEADER)); 

    //Usual stuff 
    IntPtr hOldBitmap = SelectObject(hMemDc, hBmp); 
    //Grab bitmap 
    BitBlt(hMemDc, 0, 0, bi.biWidth, bi.biHeight, hDc, 0, 0, Srccopy); 
    // Allocate memory for a copy of bitmap bits 
    byte[] RealBits = new byte[cb]; 
    // And grab bits from DIBSestion data 
    Marshal.Copy(pBits, RealBits, 0, cb); 

    // This simply creates valid bitmap file header, so it can be saved to disk 
    BITMAPFILEHEADER bfh = new BITMAPFILEHEADER(); 
    uint colorSize = 2 * 4;//2 colors for B&W, 4 bytes (RGBQUAD) 
    uint sizeofBinfo = 0x36 + colorSize;//original 
    //sizeofBINFO = (uint)Marshal.SizeOf(bi);//sorin 
    //bfh.bfSize = (uint)cb + 0x36; // Size of header + size of BITMAPINFOHEADER size of bitmap bits 
    bfh.bfSize = (uint)(cb + sizeofBinfo); 
    bfh.bfType = 0x4d42; //BM 
    bfh.bfOffBits = sizeofBinfo; // 
    int HdrSize = 14; 
    byte[] header = new byte[HdrSize]; 
    BitConverter.GetBytes(bfh.bfType).CopyTo(header, 0); 
    BitConverter.GetBytes(bfh.bfSize).CopyTo(header, 2); 
    BitConverter.GetBytes(bfh.bfOffBits).CopyTo(header, 10); 

    //Allocate enough memory for complete bitmap file 
    byte[] data = new byte[cb + bfh.bfOffBits]; 
    //BITMAPFILEHEADER 
    header.CopyTo(data, 0); 

    //BITMAPINFOHEADER 
    header = new byte[Marshal.SizeOf(bi)]; 
    IntPtr pHeader = LocalAlloc(Gptr, (uint)Marshal.SizeOf(bi)); 
    Marshal.StructureToPtr(biNew, pHeader, false); 
    Marshal.Copy(pHeader, header, 0, Marshal.SizeOf(bi)); 
    LocalFree(pHeader); 

    header.CopyTo(data, HdrSize); 

    //set black color as second color from color table 
    byte[] colors = new byte[10]; 
    colors[4] = 255; 
    colors[5] = 255; 
    colors[6] = 255; 

    colors.CopyTo(data, (int)bfh.bfOffBits - (int)colorSize); 

    //Bitmap bits 
    RealBits.CopyTo(data, (int)bfh.bfOffBits); 

    DeleteObject(SelectObject(hMemDc, hOldBitmap)); 
    DeleteDC(hMemDc); 

    return data; 
    } 

    [DllImport("coredll.dll")] 
    public static extern bool DeleteObject(IntPtr hObject); 

    [DllImport("coredll.dll")] 
    public static extern int InvalidateRect(IntPtr hwnd, IntPtr rect, int bErase); 

    [DllImport("coredll.dll")] 
    public static extern IntPtr GetDC(IntPtr hwnd); 

    [DllImport("coredll.dll")] 
    public static extern IntPtr CreateCompatibleDC(IntPtr hdc); 

    [DllImport("coredll.dll")] 
    public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc); 

    [DllImport("coredll.dll")] 
    public static extern int DeleteDC(IntPtr hdc); 

    [DllImport("coredll.dll")] 
    public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); 

    [DllImport("coredll.dll")] 
    public static extern int BitBlt(IntPtr hdcDst, int xDst, int yDst, int w, int h, IntPtr hdcSrc, int xSrc, int ySrc, int rop); 

    [DllImport("coredll.dll")] 
    private static extern IntPtr LocalAlloc(uint flags, uint cb); 

    [DllImport("coredll.dll")] 
    private static extern IntPtr LocalFree(IntPtr hMem); 

    [DllImport("coredll.dll")] 
    private static extern IntPtr CreateDIBSection(IntPtr hdc, IntPtr hdr, uint colors, ref IntPtr pBits, IntPtr hFile, uint offset); 
} 

將其保存到一個文件中執行以下操作:

byte[] data = BWImage.GetByteArray(bitmap); 
FileStream fs = new FileStream("BW.bmp", FileMode.Create); 
fs.Write(data, 0, data.Length); 
fs.Flush(); 
fs.Close(); 
+0

哇,屁股有多痛苦,我不確定這將完全符合我的需求,因爲我無法調用GetByteArray(位圖)(我還沒有位圖,我需要創建一個)。我想我需要調用CreateDIBSection然後寫入我的數據到pBits中... – Jason 2009-07-30 14:18:34