Dưới đây là một số mã có thể giúp:
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);
}
Để lưu nó vào một tập tin thực hiện như sau:
byte[] data = BWImage.GetByteArray(bitmap);
FileStream fs = new FileStream("BW.bmp", FileMode.Create);
fs.Write(data, 0, data.Length);
fs.Flush();
fs.Close();
Tôi đã xem OpenNETCF nhưng dựa trên cùng một enum. Nhìn vào cách họ thực hiện một số lớp Bitmap có thể sẽ hữu ích vì tôi khá chắc chắn tôi sẽ cần phải tự xây dựng tiêu đề bản thân mình. :( – Jason