简体   繁体   English

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

[英]Storing TBitmap in TMetaFile for smooth scaling in RTF doubles the image size

The RTF image support is very limited (Windows support, not just in Delphi), other formats than bitmaps and metafiles work but are not displayed correctly by RichEdit control. RTF图像支持非常有限(Windows支持,不仅限于Delphi),除位图和图元文件外,其他格式也可以使用,但RichEdit控件无法正确显示。

One thing I noticed is when Microsoft Word image is copied and pasted into RTF it scales smoothly as opposed to pasting image manually (as a bitmap). 我注意到的一件事是,当将Microsoft Word图像复制并粘贴到RTF中时,它的缩放比例很平滑,与手动粘贴图像(作为位图)相反。 The reason for that is that Word keeps internally a scaled preview of an image in Metafile format (along with original image) and this scaled version is copied and pasted to RTF and apparently RTF renders MetaFile images smoothly when scaled in RichText editor. 这样做的原因是Word在内部保留了图元文件格式的图像的缩放预览(以及原始图像),并且此缩放版本被复制并粘贴到RTF,并且显然,当在RichText编辑器中缩放时,RTF可以平滑地渲染MetaFile图像。

Seems like a good workaround and after implementing embedding BMP in WPF function I noticed one problem I cannot get around: the resulting WMF is double the size of a bitmap. 似乎是一个很好的解决方法,并且在WPF函数中实现了BMP嵌入之后,我注意到一个我无法解决的问题:生成的WMF是位图大小的两倍。 It looks like WMF stores paint buffer or a second copy of the image. 看起来WMF会存储绘画缓冲区或图像的第二个副本。

The code: 编码:

  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;

I call it using TBitmap as TGraphic: 我用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);

Can somebody find an explanation for this behaviour? 有人可以找到对此行为的解释吗? Is WMF storing paint buffer along with bitmap? WMF是否将绘画缓冲区与位图一起存储? How to prevent it? 怎么预防呢?

As to why your sizes may be different - once you Draw to the metafile you are turning over the responsibility to the Metafile for saving out the data. 至于大小可能为何不同的原因-绘制到图元文件后,您便将责任转移到图元文件上以保存数据。 Your bit depth could be different in the output. 您的位深度在输出中可能会有所不同。 I don't know for sure but I would also wonder if the StretchDraw call ends up saving the original input or if it saves a bitmap with the new number of pixels. 我不确定,但我也想知道StretchDraw调用是否最终保存了原始输入,或者是否保存了具有新像素数量的位图。 If the image is smaller than the size stretch draw call that could explain the difference. 如果图像小于尺寸拉伸绘图调用,则可以解释差异。 You will also have some overhead in the metafile that will not be in the saved bitmap although that should be minimal. 您也将在图元文件中有一些开销,这些开销不会在已保存的位图中,尽管这应该是最小的。

You may want to look at the MetaFile Explorer . 您可能需要查看MetaFile Explorer It is a tool that will show you the different draw commands that are embedded in the metafile. 该工具将向您显示嵌入在图元文件中的不同绘制命令。 I tried your code and looked at the resulting image in MetaFileExplorer. 我尝试了您的代码,并在MetaFileExplorer中查看了生成的图像。 The size of the embedded image looked OK to me. 嵌入图像的大小对我来说还可以。 Look at the EMR_STRETCHBLT.cbBitsSrc size. 查看EMR_STRETCHBLT.cbBitsSrc大小。 However, I do see the size difference you are reporting so something is taking that space. 但是,我确实看到了您报告的大小差异,因此正在占用该空间。

You stated: 您说:

The reason for that is that Word keeps internally a scaled preview of an image in Metafile format (along with original image) and this scaled version is copied and pasted to RTF and apparently RTF renders MetaFile images smoothly when scaled in RichText editor. 这样做的原因是Word在内部保留了图元文件格式的图像的缩放预览(以及原始图像),并且此缩放版本被复制并粘贴到RTF,并且显然,当在RichText编辑器中缩放时,RTF可以平滑地渲染MetaFile图像。

I question this assumption a little bit. 我对此假设有点质疑。 A bitmap (any raster image) will show different scaling artifacts depending on how the scaling is performed. 位图(任何光栅图像)将根据缩放的执行方式显示不同的缩放伪像。 The Image Scaling Wikipedia page has some good examples of the differences. Image Scaling Wikipedia页面上有一些很好的例子说明了差异。

When you write a bitmap into a metafile you are then letting the metafile perform the scaling when the image is drawn to the screen. 当您将位图写入图元文件时,然后在将图像绘制到屏幕上时让图元文件执行缩放。 Different algorithms used by the metafile drawing code vs. the RTF drawing code will cause the output to look different. 图元文件绘图代码与RTF绘图代码使用的不同算法将导致输出看起来不同。 However there is nothing special about a metafile that can't be done with a normal bitmap. 但是,关于图元文件,没有什么普通的位图无法完成的。 In both cases the original pixels need to be resized/resampled. 在两种情况下,都需要调整原始像素的大小/重新采样。

Metafiles were really intended for something different than just embedding a single bitmap image. 实际上,图元文件的目的是与嵌入单个位图图像不同。 Here is a good Windows Metafile FAQ that explains some of the differences. 这是一个很好的Windows图元文件常见问题解答 ,它解释了其中的一些区别。 Basically if you define lines, shapes, and text you can get really smooth scaling because you are describing the drawing, not storing individual pixels. 基本上,如果定义线条,形状和文本,则可以得到真正平滑的缩放比例,因为您是在描述图形,而不是存储单个像素。

The key part to getting a good looking scaled image is to pick the right scaling routine. 获得美观的缩放图像的关键部分是选择正确的缩放例程。 I have used the Graphics32 library for this in my application. 我在应用程序中为此使用了Graphics32库 The standard StretchDraw routines are designed to be fast - not high quality. 标准的StretchDraw例程设计得很快-而不是高质量。 When getting ready to draw to the screen I pick a Graphics32 resampler that gives me the results I want. 当准备绘制到屏幕上时,我选择了一个Graphics32重采样器,它可以为我提供所需的结果。 This can change depending on the sizes. 这可以根据大小而改变。 For example is your final output larger or smaller than the input image. 例如,您的最终输出大于或小于输入图像。 I resize the image to the final output size and then just Draw instead of StretchDraw. 我将图像调整为最终输出大小,然后仅使用Draw而不是StretchDraw。

add this code before your mf.SaveToFile('C:\\4MB_MetaFile_Why.wmf'); 在您的mf.SaveToFile('C:\\4MB_MetaFile_Why.wmf');之前添加此代码mf.SaveToFile('C:\\4MB_MetaFile_Why.wmf');

  mf.MMHeight := Round(mf.Height / Screen.PixelsPerInch * 2540);
  mf.MMWidth := Round(mf.Width / Screen.PixelsPerInch * 2540);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM