在TMetaFile中存储TBitmap以便在RTF中平滑缩放,使图像大小加倍

时间:2013-06-10 13:34:40

标签: delphi bitmap metafile wmf

RTF图像支持非常有限(Windows支持,不仅仅是在Delphi中),其他格式比位图和元文件有效但RichEdit控件无法正确显示。

我注意到的一件事是当Microsoft Word图像被复制并粘贴到RTF中时,它会缩放 平滑而不是手动粘贴图像(作为位图)。 原因是Word在内部保留了Metafile格式的图像的缩放预览(以及原始图像),并且这个缩放版本被复制并粘贴到RTF,显然RTF在RichText编辑器中缩放时可以平滑地呈现MetaFile图像。

似乎是一个很好的解决方法,在WPF函数中实现嵌入BMP后,我注意到一个我无法解决的问题:生成的WMF是位图大小的两倍。看起来WMF存储了油漆缓冲区或图像的第二个副本。

代码:

  procedure DoCopyImage(AGraphic: TGraphic; AWidth, AHeight: Integer);
  var
    mf: TMetafile;
    mfc: TMetafileCanvas;
    r: Cardinal;
  begin
    mf := TMetafile.Create;
    try
      mf.Enhanced := True;
      mf.SetSize(AWidth, AHeight);
      mfc := TMetafileCanvas.Create(mf, 0);
      try
        // set clipping region to a whole image
        r := CreateRectRgn(0, 0, AWidth, AHeight);
        try
          SelectClipRgn(mfc.Handle, r)
        finally
          DeleteObject(r);
        end;

        if (AGraphic.Width = AWidth) and (AGraphic.Height = AHeight) then
          mfc.Draw(0, 0, AGraphic)
        else
          mfc.StretchDraw(Rect(0, 0, AWidth, AHeight), AGraphic);
      finally
        mfc.Free;
      end;
      // Clipboard.Assign(mf);
      mf.SaveToFile('C:\4MB_MetaFile_Why.wmf');
    finally
      mf.Free;
    end;
  end;

我使用TBitmap称为TGraphic:

  pic := TPicture.Create;
  pic.LoadFromFile('C:\2MB_24bpp_Bitmap.bmp');
  bmp := Graphics.TBitmap.Create;
  bmp.Assign(pic.Graphic);
  bmp.Dormant; // experimentation
  bmp.FreeImage; // experimentation
  DoCopyImage(bmp, bmp.Width, bmp.Height);

有人可以找到这种行为的解释吗? WMF是否存储绘图缓冲区以及位图?怎么预防呢?

2 个答案:

答案 0 :(得分:2)

至于为什么你的尺寸可能会有所不同 - 一旦你绘制到图元文件,你就会将责任交给元文件来保存数据。您的位深度可能会在输出中有所不同。我不确定,但我也想知道StretchDraw调用是否最终保存了原始输入,或者它是否保存了具有新像素数的位图。如果图像小于拉伸绘制调用的大小,可以解释差异。您还将在元文件中有一些开销,这些开销不会在保存的位图中,尽管这应该是最小的。

您可能需要查看MetaFile Explorer。它是一个工具,它将向您显示嵌入在图元文件中的不同绘图命令。我尝试了你的代码并在MetaFileExplorer中查看了生成的图像。嵌入式图像的大小对我来说很合适。查看EMR_STRETCHBLT.cbBitsSrc大小。但是,我确实看到你报告的尺寸差异,所以有些东西占据了这个空间。

你说:

  

原因是Word内部保存了Metafile格式的图像的缩放预览(以及原始图像),这个缩放版本被复制并粘贴到RTF,显然RTF在RichText编辑器中缩放时可以平滑地呈现MetaFile图像。

我对这个假设有点质疑。位图(任何光栅图像)将根据缩放的执行方式显示不同的缩放伪像。 Image Scaling Wikipedia page有一些很好的例子。

当您将位图写入图元文件时,您可以在图像绘制到屏幕时让图元文件执行缩放。元文件绘图代码与RTF绘图代码使用的不同算法将导致输出看起来不同。但是,使用普通位图无法实现的元文件没有什么特别之处。在这两种情况下,原始像素都需要调整大小/重新采样。

图元文件的确用于与嵌入单个位图图像不同的东西。这是一个很好的Windows Metafile FAQ,它解释了一些不同之处。基本上,如果您定义线条,形状和文本,您可以获得非常平滑的缩放,因为您正在描述绘图,而不是存储单个像素。

获得好看的缩放图像的关键部分是选择正确的缩放程序。我在我的应用程序中使用了Graphics32 library。标准的StretchDraw例程设计得很快 - 而不是高质量。当准备好绘制到屏幕时,我选择了一个Graphics32重采样器,它可以提供我想要的结果。这可能会根据尺寸而改变。例如,您的最终输出大于或小于输入图像。我将图像调整为最终输出大小,然后只绘制Draw而不是StretchDraw。

答案 1 :(得分:1)

mf.SaveToFile('C:\4MB_MetaFile_Why.wmf');

之前添加此代码
  mf.MMHeight := Round(mf.Height / Screen.PixelsPerInch * 2540);
  mf.MMWidth := Round(mf.Width / Screen.PixelsPerInch * 2540);