2013-11-22 70 views

回答

4

創建第二個圖元文件。使用SetWorldTransform創建旋轉變換。將第一個圖元文件繪製到第二個元素上,然後讓其他元素完成。

+0

謝謝,大衛!像往常一樣,你可以節省很多小時的生活。 :)我在下面發佈了一個代碼示例 – Andrew

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; 
2

安德魯的 「btnRotateClick」 有的錯誤!

  1. 10-15次點擊後,執行時間快速增加。
  2. 如果用戶更改控制面板\ Display中的全局窗口設置或使用「dpiScaling.exe」,則圖像會在每次單擊時收縮。

我已經寫了一個更簡單的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; 

解的全局顯示設置改變:

如果一個用戶改變在控制面板全球顯示器設置,所有組件 窗體上的像素寬度保持相同。但屏幕寬度會發生變化。 我的顯示器有horzontaly 1680 px。 更改顯示設置後的Screen.Width返回1344 px。 在世界轉換之前,您需要更正目標元文件大小。

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; 

這correcion旋轉的圖像,不改變尺寸後。

解決方案在每一個旋轉增加執行時間。

我在這裏給出一個表格來說明問題。

累積旋轉會導致執行時間增加。

隨着累積旋轉我的意思是已經旋轉旋轉圖像。

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程序的類型。

另一種方法是更改​​每個元文件記錄的座標。 微軟在2014年發佈了:[MS-EMF] .pdf:增強型圖元文件格式。看起來很多工作。

還有其他問題。

有信息

虧損的旋轉型圖元文件已經失去了作者和說明。在旋轉之前,您不能簡單地保存此信息 並在旋轉後恢復此信息。屬性CreatedBy和描述 不可寫。 用途:

DestCanvas := TMetafileCanvas.CreateWithComment 

也看到

單元Winapi.GDIPAPI對圖元文件擴展的詳細信息。

聯機文檔EnumDisplaySettings。

備註

旋轉圖元文件作爲位圖給出了一個質量損失。 我決定複製並粘貼Andrew的代碼,但發現錯誤。 我寫了下面的代碼,但我的測試可能性很差。我只有一個EMFPLUS文件和一個監視器。測試在Windows8.1

這裏是我的代碼:(德爾福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.