我有一个签名图像,我试图保存为1 bpp位图以节省文件空间。完整的.NET Framework具有枚举PixelFormat.Format1bppIndexed
,但.NET Compact Framework不支持它。在.net紧凑框架中将图像转换为1 bpp位图
有没有人在Windows Mobile中发现了一种方法来实现这一点?
我有一个签名图像,我试图保存为1 bpp位图以节省文件空间。完整的.NET Framework具有枚举PixelFormat.Format1bppIndexed
,但.NET Compact Framework不支持它。在.net紧凑框架中将图像转换为1 bpp位图
有没有人在Windows Mobile中发现了一种方法来实现这一点?
感谢您指向正确的方向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;
}
}
即使在完整的框架中创建并保存位图位图也是有问题的。
我以前撰写过关于这个问题的文章。
http://www.codeproject.com/KB/GDI-plus/BitonalImageConverter.aspx
我在紧凑型框架的背景下重新审视这段代码,当你这样做的枚举值不存在,所以你不能从头开始创建双色调图像发现。
我很想知道您是否可以在紧凑框架中加载预先存在的双色调图像。如果您可以加载预先存在的二进制位图,则可以降低级别,并将位图图像格式直接写入磁盘或内存流,而不是使用GDI +对象,但这样做可能不是微不足道的。
我必须在过去通过蓝牙生成黑色&白色报告(颜色或灰度图像对于打印机的缓冲区来说太大)。原来,我不得不使用本机代码创建图像。
这里有一个片段:
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类没有一个保存,让你自由和清除那里。
感谢您指出我在正确的方向。我设计了一个解决方案(在下面发布),它建立在你的代码片段上,并将一个Bitmap对象转换成1位bpp位图字节流。 – jnosek 2009-12-03 22:52:00
做得好,杰克 – ctacke 2009-12-04 01:35:27
不要以为你知道你在哪里得到Win32.DeleteObject(和其他Win32方法)?我的解决方案中有一个Win32项目,但它没有这些方法。你是否自己推出了它们(或者你能记得这么久以后?) – Vaccano 2010-10-07 22:10:02
它们是来自coredll.dll的P/Invoke函数。 实施例: [的DllImport( “coredll.dll中”)] 公共静态外部空隙DeleteObject的(IntPtr的hObj); – jnosek 2010-10-08 14:48:59