2009-12-02 140 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