2009-12-02 54 views
6

我有一個簽名圖像,我試圖保存爲1 bpp位圖以節省文件空間。完整的.NET Framework具有枚舉PixelFormat.Format1bppIndexed,但.NET Compact Framework不支持它。在.net緊湊框架中將圖像轉換爲1 bpp位圖

有沒有人在Windows Mobile中發現了一種方法來實現這一點?

回答

6

感謝您指向正確的方向ctacke。 我無法使用Bitmap類保存圖像數據。它不斷扔出OutOfMemoryException。我使用BinaryWriter寫出了位圖,就像你所建議的那樣。

我的最終解決方案返回一個字節數組,使用它可以選擇要寫入到磁盤,保存到數據庫,傳輸等

class ImageHelper 
{ 
    [StructLayout(LayoutKind.Sequential)] 
    public struct BITMAPINFOHEADER 
    { 
     public BITMAPINFOHEADER(ushort bpp, int height, int width) 
     { 
      biBitCount = bpp; 
      biWidth = width; 
      biHeight = height; 

      biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER)); 
      biPlanes = 1; // must be 1 
      biCompression = 0; // no compression 
      biSizeImage = 0; // no compression, so can be 0 
      biXPelsPerMeter = 0; 
      biYPelsPerMeter = 0; 
      biClrUsed = 0; 
      biClrImportant = 0; 
     } 

     public void Store(BinaryWriter bw) 
     { 
      Store(bw, null); 
     } 

     public void Store(BinaryWriter bw, uint[] colorPalette) 
     { 
      // Must maintain order for file writing 
      bw.Write(biSize); 
      bw.Write(biWidth); 
      bw.Write(biHeight); 
      bw.Write(biPlanes); 
      bw.Write(biBitCount); 
      bw.Write(biCompression); 
      bw.Write(biSizeImage); 
      bw.Write(biXPelsPerMeter); 
      bw.Write(biYPelsPerMeter); 
      bw.Write(biClrUsed); 
      bw.Write(biClrImportant); 

      // write color palette if 8 bpp or less 
      if (biBitCount <= 8) 
      { 
       if (colorPalette == null) 
        throw new ArgumentNullException("bpp is 8 or less, color palette is required"); 

       uint paletteCount = BITMAPFILEHEADER.CalcPaletteSize(biBitCount)/4; 
       if (colorPalette.Length < paletteCount) 
        throw new ArgumentException(string.Format("bpp is 8 or less, color palette must contain {0} colors", paletteCount)); 

       foreach (uint color in colorPalette) 
        bw.Write(color); 
      } 
     } 

     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; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    public struct BITMAPFILEHEADER 
    { 
     public BITMAPFILEHEADER(BITMAPINFOHEADER info, out uint sizeOfImageData) 
     { 
      bfType = 0x4D42; // Microsoft supplied value to indicate Bitmap 'BM' 
      bfReserved1 = 0; 
      bfReserved2 = 0; 

      // calculate amount of space needed for color palette 
      uint paletteSize = CalcPaletteSize(info.biBitCount); 

      bfOffBits = 54 + paletteSize; // default value + paletteSize 

      // calculate size of image 
      sizeOfImageData = (uint)(CalcRowSize(info.biWidth * info.biBitCount) * info.biHeight); 
      bfSize = sizeOfImageData + bfOffBits; 
     } 

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

     public static uint CalcPaletteSize(int bpp) 
     { 
      // 8 bpp or less, needs an uint per color 
      if (bpp <= 8) 
       return 4 * (uint)Math.Pow(2, bpp); 

      // no palette needed for 16bpp or higher 
      return 0; 
     } 

     public void Store(BinaryWriter bw) 
     { 
      // Must maintain order for file writing 
      bw.Write(bfType); 
      bw.Write(bfSize); 
      bw.Write(bfReserved1); 
      bw.Write(bfReserved2); 
      bw.Write(bfOffBits); 
     } 

     public ushort bfType; 
     public uint bfSize; 
     public short bfReserved1; 
     public short bfReserved2; 
     public uint bfOffBits; 
    } 

    public static byte[] GetByteArray(Bitmap image) 
    { 
     IntPtr hbmOld; 
     IntPtr hBitmap; 
     IntPtr hDC; 

     // create infoheader 
     BITMAPINFOHEADER bih = new BITMAPINFOHEADER(1, image.Height, image.Width); 
     // set black and white for 1 bit color palette 

     // create fileheader and get data size 
     uint sizeOfImageData; 
     BITMAPFILEHEADER bfh = new BITMAPFILEHEADER(bih, out sizeOfImageData); 

     // create device context in memory 
     hDC = Win32.CreateCompatibleDC(IntPtr.Zero); 

     // create a 1 bpp DIB 
     IntPtr pBits = IntPtr.Zero; 
     hBitmap = Win32.CreateDIBSection(hDC, ref bih, 1, ref pBits, IntPtr.Zero, 0); 

     // selet DIB into device context 
     hbmOld = Win32.SelectObject(hDC, hBitmap); 

     using (Graphics g = Graphics.FromHdc(hDC)) 
     { 
      g.DrawImage(image, 0, 0); 
     } 

     byte[] imageData = new byte[sizeOfImageData]; 
     byte[] fileData; 

     using (MemoryStream ms = new MemoryStream((int)bfh.bfSize)) 
     { 
      using (BinaryWriter w = new BinaryWriter(ms)) 
      { 
       bfh.Store(w); 
       // store bitmapinfoheader with 1 bpp color palette for black and white 
       bih.Store(w, new uint[] { (uint)0x0, (uint)0xffffff }); 

       // copy image data into imageData buffer 
       Marshal.Copy(pBits, imageData, 0, imageData.Length); 

       // write imageData to stream 
       w.Write(imageData); 

       w.Close(); 
      } 

      fileData = ms.GetBuffer(); 
      ms.Close(); 
     } 

     // select old object 
     if (hbmOld != IntPtr.Zero) 
      Win32.SelectObject(hDC, hbmOld); 

     // delete memory bitmap 
     if (hBitmap != IntPtr.Zero) 
      Win32.DeleteObject(hBitmap); 

     // delete memory device context 
     if (hDC != IntPtr.Zero) 
      Win32.DeleteDC(hDC); 

     return fileData; 
    } 
} 
+0

做得好,傑克 – ctacke 2009-12-04 01:35:27

+0

不要以爲你知道你在哪裏得到Win32.DeleteObject(和其他Win32方法)?我的解決方案中有一個Win32項目,但它沒有這些方法。你是否自己推出了它們(或者你能記得這麼久以後?) – Vaccano 2010-10-07 22:10:02

+2

它們是來自coredll.dll的P/Invoke函數。 實施例: [的DllImport( 「coredll.dll中」)] 公共靜態外部空隙DeleteObject的(IntPtr的hObj); – jnosek 2010-10-08 14:48:59

1

即使在完整的框架中創建並保存位圖位圖也是有問題的。

我以前撰寫過關於這個問題的文章。

http://www.codeproject.com/KB/GDI-plus/BitonalImageConverter.aspx

我在緊湊型框架的背景下重新審視這段代碼,當你這樣做的枚舉值不存在,所以你不能從頭開始創建雙色調圖像發現。

我很想知道您是否可以在緊湊框架中加載預先存在的雙色調圖像。如果您可以加載預先存在的二進制位圖,則可以降低級別,並將位圖圖像格式直接寫入磁盤或內存流,而不是使用GDI +對象,但這樣做可能不是微不足道的。

11

我必須在過去通過藍牙生成黑色&白色報告(顏色或灰度圖像對於打印機的緩衝區來說太大)。原來,我不得不使用本機代碼創建圖像。

這裏有一個片段:

private void CreateUnmanagedResources() 
{ 
    // for safety, clean up anything that was already allocated 
    ReleaseUnmanagedResources(); 

    bih = new BITMAPINFOHEADER(); 
    bih.biBitCount = 1; 
    bih.biClrImportant = 0; 
    bih.biClrUsed = 0; 
    bih.biCompression = 0; 
    bih.biHeight = m_cy; 
    bih.biPlanes = 1; 
    bih.biSize = (uint)(Marshal.SizeOf(typeof(BITMAPINFOHEADER)) - 8); 
    bih.biSizeImage = 0; 
    bih.biWidth = m_cx; 
    bih.biXPelsPerMeter = 0; 
    bih.biYPelsPerMeter = 0; 
    bih.clr2 = 0xffffff; 
    bih.clr1 = 0x0; 

    hDC = Win32.CreateCompatibleDC(IntPtr.Zero); 
    pBits = IntPtr.Zero; 
    hBitmap = Win32.CreateDIBSection(hDC, bih, 1, ref pBits, IntPtr.Zero, 0); 
    hbmOld = Win32.SelectObject(hDC, hBitmap); 
} 

private void ReleaseUnmanagedResources() 
{ 
    if (hbmOld != IntPtr.Zero) 
     Win32.SelectObject(hDC, hbmOld); 

    if(hBitmap != IntPtr.Zero) 
     Win32.DeleteObject(hBitmap); 

    if (hDC != IntPtr.Zero) 
     Win32.DeleteDC(hDC); 
} 

然後我用Graphics.FromHdc獲得管理圖形對象,我就可以畫畫了報告到。

我沒有用的BinaryWriter節省,但在CF1.0天當Bitmap類沒有一個保存,讓你自由和清除那裏。

+0

感謝您指出我在正確的方向。我設計了一個解決方案(在下面發佈),它建立在你的代碼片段上,並將一個Bitmap對象轉換成1位bpp位圖字節流。 – jnosek 2009-12-03 22:52:00