是否可以旋转元文件图像而不将其转换为位图?

时间:2013-11-22 01:50:38

标签: delphi rotation metafile

我需要旋转TMetafile图像,至少旋转90度。 通过在位图画布上绘制我的图元文件然后旋转位图很容易旋转它,但我更喜欢将其保持为矢量图像格式。这有可能吗?如果是,那我该怎么做?

3 个答案:

答案 0 :(得分:4)

创建第二个图元文件。使用SetWorldTransform创建旋转变换。将第一个图元文件绘制到第二个元文件上,然后让转换完成其余的工作。

答案 1 :(得分:2)

" btnRotateClick"安德鲁有错误!

  1. 10-15次点击后快速增加执行时间。
  2. 如果用户更改控制面板\ Display中的全局窗口设置或使用" dpiScaling.exe"每次点击都会缩小图像。
  3. 我编写了一个更简单的btnRotateClick版本,它可以避免使用div 2舍入错误,但错误仍然存​​在。

    procedure TfrmPreviewImage.RotateMetafile(ClockWise: Boolean);
    // Not risolved: Fast increasing rotation time after about 15 rotations.
    // Not resolved: Control panel Screen Change dimension of all elements
    var
      DestMetafile: TMetafile;
      DestCanvas: TMetafileCanvas;
      TransformMatrix: XFORM;
    begin
    
      Assert(Image1.Picture.Graphic is TMetafile);
    
      DestMetafile := TMetafile.Create;
      DestMetafile.Enhanced := True;
      DestMetafile.SetSize(Image1.Picture.Metafile.Height, Image1.Picture.Metafile.Width);
      try
        DestCanvas := TMetafileCanvas.Create(DestMetafile, Canvas.Handle);
        DestCanvas.Lock;
        Try
    
          SetGraphicsMode(DestCanvas.Handle, GM_ADVANCED);
          SetMapMode(DestCanvas.Handle, MM_TEXT);
    
          if ClockWise then
          begin
            Fillchar(TransformMatrix, SizeOf(TransformMatrix), 0);
            // Angle := DegToRad(90);
            TransformMatrix.eM11 := 0;  // Cos(Angle);
            TransformMatrix.eM12 := -1; // Sin(Angle);
            TransformMatrix.eM21 := 1;  // -Sin(Angle);
            TransformMatrix.eM22 := 0;  // Cos(Angle);
            TransformMatrix.eDx := 0;
            TransformMatrix.eDy := image1.Picture.Metafile.Width;
          end
          else
          begin
            Fillchar(TransformMatrix, SizeOf(TransformMatrix), 0);
            // Angle := DegToRad(90);
            TransformMatrix.eM11 := 0;  // Cos(Angle);
            TransformMatrix.eM12 := 1;  // Sin(Angle);
            TransformMatrix.eM21 := -1; // -Sin(Angle);
            TransformMatrix.eM22 := 0;  // Cos(Angle);
            TransformMatrix.eDx := image1.Picture.Metafile.Height;
            TransformMatrix.eDy := 0;
          end;
          SetWorldTransform(DestCanvas.Handle, TransformMatrix);
    
          DestCanvas.Draw(0, 0, Image1.Picture.Graphic);
    
        Finally
          DestCanvas.Unlock;
          DestCanvas.Free();
        End;
    
        Image1.Picture.Metafile.Assign(DestMetafile);
      finally
        DestMetafile.Free;
      end;
    end;
    

    更改全局显示设置的解决方案:

    如果用户更改控制面板中的全局显示设置,则为所有组件的像素宽度 在表格上保持不变。但屏幕宽度会发生变化。 我的显示器有1680像素。 更改显示设置后的Screen.Width返回1344像素。 在进行世界转换之前,您需要更正目标图元文件大小。

    w1 := MulDiv(w, Screen.Width, ScreenSize.cx);
    h1 := MulDiv(h, Screen.Height, ScreenSize.cy);
    DestMetafile.SetSize(h1, w1);
    

    经过长时间的搜索才能了解真实的'我找到的屏幕尺寸:

    const
       ENUM_CURRENT_SETTINGS: DWORD = $FFFFFFFF;
    
    EnumDisplaySettings(nil, ENUM_CURRENT_SETTINGS, DevMode);
    
    ScreenSize.cx := DevMode.dmPelsWidth;   
    

    在此更正之后,旋转的图像不会改变尺寸。

    每次轮换时增加执行时间的解决方案。

    我在这里给出一个表来说明问题。

    累积旋转会导致执行时间增加。

    累积旋转我的意思是旋转已旋转的图像。

       ENHMETAHEADER                     Size
      Angle   nHandles     nRecords     (Bytes)
          0          4          173      4192
         90          7          214      5372
        180         10          273      6998
        ...
        450         19          692     20064
        540         22         1081     36864
    

    要避免这种情况,请不要旋转已旋转的图像,而是旋转原始图像。

    问题的答案取决于您使用SetWorldTransform时编写的程序类型。

    另一种方法是更改​​每个元文件记录的坐标。 Microsoft于2014年发布:[MS-EMF] .pdf:增强的元文件格式。似乎做了很多工作。

    还有其他问题。

    信息丢失

    旋转的Metafile失去了作者和描述。您不能简单地保存此信息 在旋转之前旋转并恢复此信息。属性CreatedBy和Description 是不可写的。 使用:

    DestCanvas := TMetafileCanvas.CreateWithComment
    

    另见

    单元Winapi.GDIPAPI有关元文件扩展的更多信息。

    在线文档EnumDisplaySettings。

    <强>说明

    将图元文件作为位图旋转会导致质量损失。 我决定复制并粘贴安德鲁的代码,但发现了错误。 我编写了下面的代码,但我的测试可能性很小。我只有一个EMFPLUS文件和 一个监视器。在Windows8.1上测试

    这是我的代码:(Delphi XE3)

    unit UEmfRotate;
    {
    
      Use:
      var
        FRotationPos : TRotationPosition;
    
        FRotationPos := GetNewPosition(Clockwise, FRotationPos);
        UEmfRotate.RotateMetafile(Filename, image1, FRotationPos);
    
    }
    
    interface
    
    uses
      System.Types, Vcl.ExtCtrls;
    
    type
      TRotationPosition = -3 .. 3;
    
    procedure RotateMetafile(const Path: string; image: TImage;
      Position: TRotationPosition);
    function GetNewPosition(Clockwise: boolean;
      OldPosition: TRotationPosition): TRotationPosition;
    
    implementation
    
    uses
      Winapi.Windows, Vcl.Graphics, Vcl.Forms, System.Math;
    {
      // Resolved: Fast increasing rotation time after about 15 rotations.
      // Resolved: Control panel Display Change (dimension of all elements)
      // Resolved: Loose of CreatedBy and Description after rotation
    }
    
    {
    
      All destination positions from -3 to 3 (-270.. 270)
      0     1      2     3
      WWW   AW    AAA    WA
      AAA   AW    WWW    WA
            AW           WA
    
      0     -1    -2     -3
      WWW   WA    AAA     AW
      AAA   WA    WWW     AW
            WA            AW
    
    }
    
    type
      TDestinationArray = array [boolean, TRotationPosition] of TRotationPosition;
      TDegrees = array [TRotationPosition] of cardinal;
    
    const                     // OldPosition  -3  -2  -1   0  -1  -2  -3    Clockwise
      DestinationArray: TDestinationArray = (( 0, -3, -2, -1,  0,  1,  2),  // False
                                             (-2, -1,  0,  1,  2,  3,  0)); // True
              // Position  -3,  -2,  -1, 0,  1,   2,   3
      Degrees: TDegrees = (90, 180, 270, 0, 90, 180, 270);
    
    function GetNewPosition(Clockwise: boolean;
      OldPosition: TRotationPosition): TRotationPosition;
    begin
      Result := DestinationArray[Clockwise, OldPosition];
    end;
    
    function GetDegrees(Position: Integer): cardinal;
    begin
      Result := Degrees[Position];
    end;
    
    function GetScreenSize(out Size: System.Types.TSize): boolean;
    // Used to correct for a change in windows global display settings.
    const
      ENUM_CURRENT_SETTINGS: DWORD = $FFFFFFFF;
    var
      DevMode: TDevMode;
    begin
      Size.cx := 0;
      Size.cy := 0;
      DevMode.dmSize := SizeOf(TDevMode);
      Result := EnumDisplaySettings(nil, ENUM_CURRENT_SETTINGS, DevMode);
      if Result then
      begin
        Size.cx := DevMode.dmPelsWidth;
        Size.cy := DevMode.dmPelsHeight;
      end;
    end;
    
    procedure RotateMetafile90(image: TImage);
    var
      DestMetafile: TMetafile;
      DestCanvas: TMetafileCanvas;
      TransformMatrix: XFORM;
      w, h: Integer;
      w1, h1: Integer;
      ScreenSize: System.Types.TSize;
    begin
    
      w := image.Picture.Width;
      h := image.Picture.Height;
      // Get screen dimension independent of the control panel display settings.
      if GetScreenSize(ScreenSize) then
      begin
        w1 := MulDiv(w, Screen.Width, ScreenSize.cx);
        h1 := MulDiv(h, Screen.Height, ScreenSize.cy);
      end
      else
      begin
        // Can not do anything
        w1 := w;
        h1 := h;
      end;
      DestMetafile := TMetafile.Create;
      DestMetafile.Enhanced := True;
      DestMetafile.SetSize(h1, w1);
      try
        DestCanvas := TMetafileCanvas.CreateWithComment(DestMetafile, 0,
          image.Picture.Metafile.CreatedBy, image.Picture.Metafile.Description);
        DestCanvas.Lock;
        Try
    
          SetGraphicsMode(DestCanvas.Handle, GM_ADVANCED);
          SetMapMode(DestCanvas.Handle, MM_TEXT);
    
          Fillchar(TransformMatrix, SizeOf(TransformMatrix), 0);
          TransformMatrix.eM11 := 0;  // Cos(Angle);
          TransformMatrix.eM12 := 1;  // Sin(Angle);
          TransformMatrix.eM21 := -1; // -Sin(Angle);
          TransformMatrix.eM22 := 0;  // Cos(Angle);
          TransformMatrix.eDx := h;
          TransformMatrix.eDy := 0;
    
          SetWorldTransform(DestCanvas.Handle, TransformMatrix);
    
          DestCanvas.Draw(0, 0, image.Picture.Graphic); // Same as Play
    
        Finally
          DestCanvas.Unlock;
          DestCanvas.Free();
        End;
    
        image.Picture := nil;
        image.Picture.Metafile.Assign(DestMetafile);
    
      finally
        DestMetafile.Free;
      end;
    end;
    
    procedure RotateMetafile180(image: TImage);
    var
      DestMetafile: TMetafile;
      DestCanvas: TMetafileCanvas;
      TransformMatrix: XFORM;
      w, h: Integer;
      w1, h1: Integer;
      ScreenSize: System.Types.TSize;
    begin
    
      w := image.Picture.Width;
      h := image.Picture.Height;
      // Get screen dimension independent of the control panel display settings.
      if GetScreenSize(ScreenSize) then
      begin
        w1 := MulDiv(w, Screen.Width, ScreenSize.cx);
        h1 := MulDiv(h, Screen.Height, ScreenSize.cy);
      end
      else
      begin
        // Can not do anything
        w1 := w;
        h1 := h;
      end;
      DestMetafile := TMetafile.Create;
      DestMetafile.Enhanced := True;
      DestMetafile.SetSize(w1, h1);
      try
        DestCanvas := TMetafileCanvas.CreateWithComment(DestMetafile, 0,
          image.Picture.Metafile.CreatedBy, image.Picture.Metafile.Description);
        DestCanvas.Lock;
        Try
    
          SetGraphicsMode(DestCanvas.Handle, GM_ADVANCED);
          SetMapMode(DestCanvas.Handle, MM_TEXT);
    
          Fillchar(TransformMatrix, SizeOf(TransformMatrix), 0);
          TransformMatrix.eM11 := -1; // Cos(Angle);
          TransformMatrix.eM12 := 0;  // Sin(Angle);
          TransformMatrix.eM21 := 0;  // -Sin(Angle);
          TransformMatrix.eM22 := -1; // Cos(Angle);
          TransformMatrix.eDx := w;
          TransformMatrix.eDy := h;
    
          SetWorldTransform(DestCanvas.Handle, TransformMatrix);
    
          DestCanvas.Draw(0, 0, image.Picture.Graphic); // Same as Play
    
        Finally
          DestCanvas.Unlock;
          DestCanvas.Free();
        End;
    
        image.Picture := nil;
        image.Picture.Metafile.Assign(DestMetafile);
    
      finally
        DestMetafile.Free;
      end;
    end;
    
    procedure RotateMetafile270(image: TImage);
    var
      DestMetafile: TMetafile;
      DestCanvas: TMetafileCanvas;
      TransformMatrix: XFORM;
      w, h: Integer;
      w1, h1: Integer;
      ScreenSize: System.Types.TSize;
    begin
    
      w := image.Picture.Width;
      h := image.Picture.Height;
      // Get screen dimension independent of the control panel display settings.
      if GetScreenSize(ScreenSize) then
      begin
        w1 := MulDiv(w, Screen.Width, ScreenSize.cx);
        h1 := MulDiv(h, Screen.Height, ScreenSize.cy);
      end
      else
      begin
        // Can not do anything
        w1 := w;
        h1 := h;
      end;
      DestMetafile := TMetafile.Create;
      DestMetafile.Enhanced := True;
      DestMetafile.SetSize(h1, w1);
      try
        DestCanvas := TMetafileCanvas.CreateWithComment(DestMetafile, 0,
          image.Picture.Metafile.CreatedBy, image.Picture.Metafile.Description);
        DestCanvas.Lock;
        Try
    
          SetGraphicsMode(DestCanvas.Handle, GM_ADVANCED);
          SetMapMode(DestCanvas.Handle, MM_TEXT);
    
          Fillchar(TransformMatrix, SizeOf(TransformMatrix), 0);
          TransformMatrix.eM11 := 0;  // Cos(Angle);
          TransformMatrix.eM12 := -1; // Sin(Angle);
          TransformMatrix.eM21 := 1;  // -Sin(Angle);
          TransformMatrix.eM22 := 0;  // Cos(Angle);
          TransformMatrix.eDx := 0;
          TransformMatrix.eDy := w;
    
          SetWorldTransform(DestCanvas.Handle, TransformMatrix);
    
          DestCanvas.Draw(0, 0, image.Picture.Graphic); // Same as Play
    
        Finally
          DestCanvas.Unlock;
          DestCanvas.Free();
        End;
    
        image.Picture := nil;
        image.Picture.Metafile.Assign(DestMetafile);
    
      finally
        DestMetafile.Free;
      end;
    end;
    
    procedure RotateMetafile(const Path: string; image: TImage;
      Position: TRotationPosition);
    {
      Cumulative rotating causes increasing execution time
      With cumulative rotating i mean rotate an image already rotated
             ENHMETAHEADER         Size
      Angle  nHandles nRecords   (Bytes)
      0           4       173      4192
      90          7       214      5372
      180        10       273      6998
      270        13       354      9352
      360        16       479     13212
      450        19       692     20064
      540        22      1081     36864
    
      To avoid this never rotate an already rotated image, but rotate the
      original image.
    
    }
    begin
      image.Picture.Metafile.LoadFromFile(Path);
      Assert(image.Picture.Graphic is TMetafile);
      case GetDegrees(Position) of
        90:
          RotateMetafile90(image);
        180:
          RotateMetafile180(image);
        270:
          RotateMetafile270(image);
      end;
      // image.Picture.SaveToFile('emf.emf');
    end;
    
    end.
    

答案 2 :(得分:0)

工作代码示例,由David的建议制作。每次单击按钮都会将存储在TImage内部的图元文件旋转90度。

procedure TfMain.btnRotateClick(Sender: TObject);
var
    SourceMetafile: TMetafile;
    DestMetafile: TMetafile;
    DestCanvas: TMetafileCanvas;
    TransformMatrix: XFORM;
    Angle: Double;
begin
    Assert(imgRender.Picture.Graphic is TMetafile);
    SourceMetafile := imgRender.Picture.Graphic as TMetafile;
    DestMetafile := TMetafile.Create();
    DestMetafile.Width := SourceMetafile.Height;
    DestMetafile.Height := SourceMetafile.Width;
    try
        DestCanvas := TMetafileCanvas.Create(DestMetafile, Canvas.Handle);
        try
            SetGraphicsMode(DestCanvas.Handle, GM_ADVANCED);

            ZeroMemory(@TransformMatrix, SizeOf(TransformMatrix));
            TransformMatrix.eM11 := 1;
            TransformMatrix.eM12 := 0;
            TransformMatrix.eM21 := 0;
            TransformMatrix.eM22 := 1;
            TransformMatrix.eDx := -SourceMetafile.Width div 2;
            TransformMatrix.eDy := -SourceMetafile.Height div 2;
            SetWorldTransform(DestCanvas.Handle, TransformMatrix);

            ZeroMemory(@TransformMatrix, SizeOf(TransformMatrix));
            Angle := DegToRad(90);
            TransformMatrix.eM11 := Cos(Angle);
            TransformMatrix.eM12 := Sin(Angle);
            TransformMatrix.eM21 := -Sin(Angle);
            TransformMatrix.eM22 := Cos(Angle);
            TransformMatrix.eDx := 0;
            TransformMatrix.eDy := 0;
            ModifyWorldTransform(DestCanvas.Handle, TransformMatrix, MWT_RIGHTMULTIPLY);

            ZeroMemory(@TransformMatrix, SizeOf(TransformMatrix));
            TransformMatrix.eM11 := 1;
            TransformMatrix.eM12 := 0;
            TransformMatrix.eM21 := 0;
            TransformMatrix.eM22 := 1;
            TransformMatrix.eDx := SourceMetafile.Height div 2;
            TransformMatrix.eDy := SourceMetafile.Width div 2;
            ModifyWorldTransform(DestCanvas.Handle, TransformMatrix, MWT_RIGHTMULTIPLY);

            DestCanvas.Draw(0, 0, SourceMetafile);
        finally
            DestCanvas.Free();
        end;

        imgRender.Picture.Assign(DestMetafile);
    finally
        DestMetafile.Free();
    end;
end;