0

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.

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). 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.

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. It looks like WMF stores paint buffer or a second copy of the image.

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:

  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? How to prevent it?

too
  • 3,009
  • 4
  • 37
  • 51

2 Answers2

2

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. 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. 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. The size of the embedded image looked OK to me. Look at the EMR_STRETCHBLT.cbBitsSrc size. 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.

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.

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. 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. 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. The standard StretchDraw routines are designed to be fast - not high quality. When getting ready to draw to the screen I pick a Graphics32 resampler that gives me the results I want. 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.

Mark Elder
  • 3,987
  • 1
  • 31
  • 47
  • The original problem is that when plain 24bpp bitmap is pasted into RTF, when scaled down you can see the artifacts as (probably for compatibility reasons) people at Microsoft decided to keep original scaling routine. It seems as well that embedding the bitmap in WMF/EMF allows the same image to be scaled smoothly (another possible decision at Microsoft to update original scaling algorithm for images painted in EMF queue). The problem is how to create such metafile so it won't take twice as much space ? In mfexplorer EMR_HEADER contains expected nBytes but file is twice the size. Many thanks. – too Jun 11 '13 at 13:07
  • To clarify some shortcuts above - "original scaling routine" means probably nearest-neighbour scaling chosen when RTF file format was incepted and first version implemented. – too Jun 11 '13 at 13:17
  • The scaling routine is not part of the RTF file format spec. It is part of the implementation. Saving an RTF file in Word and then view it in Write.exe (WordPad) and your images will look different. Your results may also vary between TRichEdit, TjvRichEdit, TcxRichEdit, TRichView, etc. My point is since you are already messing with the image to put it into a Metafile you could determine the final size when pasting and resample the image before adding it to the document. – Mark Elder Jun 11 '13 at 15:16
  • You're right, comctl32.dll is reused by all RichEdit controls I've seen but from what I know it has no hooks for custom picture rendering implementations and that might be the reason Microsoft decided to copy WMF instead of BMP - same it does for tables - so RTF applications can make use of graphics and so it will look better when scaled. When resampling image will loose original quality (even smooth resize using GraphUtil.ScaleImage). I already added an option to scale the image before placing it in WMF but the result is always the same - WMF roughly twice as big as BMP being embedded in it. – too Jun 12 '13 at 07:52
1

add this code before your mf.SaveToFile('C:\4MB_MetaFile_Why.wmf');

  mf.MMHeight := Round(mf.Height / Screen.PixelsPerInch * 2540);
  mf.MMWidth := Round(mf.Width / Screen.PixelsPerInch * 2540);
bummi
  • 27,123
  • 14
  • 62
  • 101