首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将Drawing.Bitmap转换为Imaging.Metafile -像素完美

将Drawing.Bitmap转换为Imaging.Metafile -像素完美
EN

Stack Overflow用户
提问于 2010-10-02 15:48:55
回答 3查看 4.6K关注 0票数 4

我试图将Drawing.Bitmap转换为Imaging.Metafile,以便将图元文件插入到Forms.RichTextBox中。(作为参考,在图元文件中嵌入位图是将位图放入富文本的推荐做法(参见richtext (RTF)规范版本1.9.1,第149页)。不幸的是,它似乎也是将图像嵌入到Forms.RichTextBox中的唯一方法,因为我无法让任何与设备相关或与设备无关的方法将位图插入到RichTextBox中。)

稍后,我必须从图元文件中检索像素数据。我要求图元文件的像素与位图的像素完全匹配。然而,当我执行转换时,像素会略有改变。(可能是由于GDI图像颜色管理(ICM)?)

以下是我的技巧:

代码语言:javascript
复制
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))
        {
            //imageGraphics.DrawImage(bitmap, new Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height));
            imageGraphics.DrawImageUnscaled(bitmap, new Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height));
        }
        rtfBoxGraphics.ReleaseHdc(pDeviceContext);
    }
    return metafile;
}

在这种情况下,我以这种方式访问图元文件的像素:

代码语言:javascript
复制
metafile.Save(stream, Imaging.ImageFormat.Png);
Bitmap bitmap = new Bitmap(stream, false);
bitmap.GetPixel(x, y);

我也尝试过使用BitBlt技术,但没有成功。

BitBlt技术:

代码语言:javascript
复制
[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;
}

我甚至不确定这种技术是否正确地复制像素数据。如果以后尝试访问图元文件中的数据,此技术将失败:

代码语言:javascript
复制
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);

如何在不改变像素的情况下将位图转换为图元文件,然后再检索像素数据?谢谢!

设置位图分辨率

这就是我如何尝试设置位图分辨率,以便图元文件的分辨率匹配:

代码语言:javascript
复制
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是同一个发送给BitmapToMetafileViaGraphicsDrawImage的。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-10-04 17:59:54

您可以手动创建和处理图元文件(例如枚举其记录),在这种情况下,您实际上可以将包含其确切数据的位图插入到元文件流中。然而,这并不像听起来那么简单。

当使用GDI图元文件时,所有操作都在内部转换为GDI+,这实际上是一个完全不同的API,它处理许多事情都是不同的。不幸的是,一旦图元文件具有某种复杂性或需要转换输出,它的内置方式就是让GDI在低分辨率位图上呈现图元文件,然后绘制它,这永远不会给出您想要的结果。

对于一个项目(关闭源代码-所以不要麻烦地询问源代码;),我必须实现一个完整的GDI- to -GDI+回放引擎类似于EMFExplorer的功能,但只使用托管代码,并与单一字符重新对齐,以准确的文本输出。这只是说这是可以做到的,如果您愿意投入一些时间来处理您需要的所有元文件记录(这应该只是一个小子集,但仍然如此)。

编辑以解决评论中提出的问题:

图元文件只不过是一系列绘图指令,基本上是GDI(+)操作的记录。因此,您应该能够将图元文件构造为二进制数据,基本上只由头和位图组成,在这种情况下(因为您不需要通过钻操作,而是始终在二进制级别处理位图),您将能够从您写入的图元文件中检索完全相同的数据。

幸运的是,自几年以来,微软一直在记录它们的文件格式和协议,并将其提供给公众,这样就可以获得WMF/EMF文件格式的精确文档。图元文件格式由MS在这里解释:http://msdn.microsoft.com/en-us/library/cc230514(PROT.10).aspx

It结构概述如下:http://msdn.microsoft.com/en-us/library/cc230516(v=PROT.10).aspx

位图记录在这里描述:http://msdn.microsoft.com/en-us/library/cc231160(v=PROT.10).aspx

使用这些信息,您应该能够将二进制数据(可能使用BinaryWriter)放在一起,然后加载/枚举文件以获取数据(例如,在图元文件中再次查找位图并从中提取所需的数据)。

票数 4
EN

Stack Overflow用户

发布于 2010-10-04 17:46:52

这只是在黑暗中的一次尝试,但是看看一些ImageFlags标志值,看看它们是否可以合并到元文件的生成或呈现中。

票数 0
EN

Stack Overflow用户

发布于 2010-10-04 23:00:54

这是一个很有趣的解决方法,但是如果你只需要把它放在RichTextBox中-你可以使用剪贴板:

代码语言:javascript
复制
    private void button1_Click(object sender, EventArgs e)
    {
        System.Drawing.Bitmap bmp = new Bitmap("g.bmp");
        Clipboard.SetData(DataFormats.Bitmap, bmp);

        richTextBox1.Paste();
    }

但我不确定相反的方法(从RTF文本中读取位图)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3846448

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档