I am trying to convert Drawing.Bitmap to Imaging.Metafile in order to insert a metafile into Forms.RichTextBox . (For reference, inserting a bitmap into a metafile is the recommended practice for placing a bitmap in a richtext (see RTF specification), version 1.9.1, page 149.) Unfortunately, this is also the only way to embed an image in Forms.RichTextBox, so as I cannot get any device-dependent or device-independent methods for inserting a bitmap into a RichTextBox to work.)
Later I have to get the pixel data from the metafile. I require that the pixels of the metafile exactly match the pixels of the bitmap. However, when I do the conversion, the pixels are slightly changed. (Perhaps due to GDI Image Color Management (ICM)?)
Here is my technique:
public static Imaging.Metafile BitmapToMetafileViaGraphicsDrawImage(Forms.RichTextBox rtfBox, Drawing.Bitmap bitmap) { Imaging.Metafile metafile; using (IO.MemoryStream stream = new IO.MemoryStream()) using (Drawing.Graphics rtfBoxGraphics = rtfBox.CreateGraphics()) { IntPtr pDeviceContext = rtfBoxGraphics.GetHdc(); metafile = new Imaging.Metafile(stream, pDeviceContext); using (Drawing.Graphics imageGraphics = Drawing.Graphics.FromImage(metafile)) {
In this case, I access the pixels of the metafile this way:
metafile.Save(stream, Imaging.ImageFormat.Png); Bitmap bitmap = new Bitmap(stream, false); bitmap.GetPixel(x, y);
I also tried using the BitBlt technique without success.
BitBlt Technology:
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")] static extern int BitBlt( IntPtr hdcDest, // handle to destination DC (device context) int nXDest, // x-coord of destination upper-left corner int nYDest, // y-coord of destination upper-left corner int nWidth, // width of destination rectangle int nHeight, // height of destination rectangle IntPtr hdcSrc, // handle to source DC int nXSrc, // x-coordinate of source upper-left corner int nYSrc, // y-coordinate of source upper-left corner System.Int32 dwRop // raster operation code ); public static Imaging.Metafile BitmapToMetafileViaBitBlt(Forms.RichTextBox rtfBox, Drawing.Bitmap bitmap) { const int SrcCopy = 0xcc0020; Graphics bitmapGraphics = Graphics.FromImage(bitmap); IntPtr pBitmapDeviceContext = bitmapGraphics.GetHdc(); RectangleF rect = new RectangleF(new PointF(0, 0), new SizeF(bitmap.Width, bitmap.Height)); Imaging.Metafile metafile = new Imaging.Metafile(pBitmapDeviceContext, rect); Graphics metafileGraphics = Graphics.FromImage(metafile); IntPtr metafileDeviceContext = metafileGraphics.GetHdc(); BitBlt(pBitmapDeviceContext, 0, 0, bitmap.Width, bitmap.Height, metafileDeviceContext, 0, 0, SrcCopy); return metafile; }
I'm not even sure if this method copies pixel data correctly. This method does not work when I try to access the data in the metafile later:
IntPtr h = metafile.GetHenhmetafile(); // ArgumentException "Parameter is not valid." byte[] data; uint size = GetEnhMetaFileBits(h, 0, out data); data = new byte[size]; GetEnhMetaFileBits(h, size, out data); stream = new IO.MemoryStream(data);
How to convert a bitmap to a metafile without changing pixels, and then get the pixel data again? Thanks!
Bitmap setup
This is how I try to set the raster resolution so that the metafile resolution matches:
Drawing.Bitmap bitmap = new Drawing.Bitmap(width, height, Imaging.PixelFormat.Format32bppArgb); // Use 32-bit pixels so that each component (ARGB) matches up with a byte // Try setting the resolution to see if that helps with conversion to/from metafiles Drawing.Graphics rtfGraphics = rtfBox.CreateGraphics(); bitmap.SetResolution(rtfGraphics.DpiX, rtfGraphics.DpiY); // Set the pixel data ... // Return the bitmap return bitmap;
RtfBox is the same as in BitmapToMetafileViaGraphicsDrawImage.