【问题标题】:Center a rotated image using GDI+使用 GDI+ 居中旋转图像
【发布时间】:2017-04-11 02:32:42
【问题描述】:

如何使用 GDI+ 使旋转的图像居中?

我在这里创建了一个小示例来说明我的问题。

图形界面

带有 OpenPictureDialog 和 PaintBox 的空白表单,与客户端对齐。向 PaintBox 添加一个 DoubleClick 事件和一个 OnPaintEvent。

背后的代码

unit Unit1;

interface

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Vcl.ExtCtrls, Vcl.ExtDlgs, GDIPAPI, GDIPOBJ, GDIPUTIL;

type
  TForm1 = class(TForm)
    PaintBox1: TPaintBox;
    OpenPictureDialog1: TOpenPictureDialog;
    procedure PaintBox1Paint(Sender: TObject);
    procedure PaintBox1DblClick(Sender: TObject);
  private
    { Private declarations }
    FImage: TGPImage;
    procedure DrawImage(aMaxWidth, aMaxHeight: Cardinal); overload;
    procedure DrawImage; overload;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation


{$R *.dfm}

procedure TForm1.DrawImage(aMaxWidth, aMaxHeight: Cardinal);
var
  Ratio: Double;
  Height, Width, HeightOffset, WidthOffset: Cardinal;
begin
  PaintBox1.Canvas.Brush.Color := clWhite;
  FillRect(PaintBox1.Canvas.Handle, Rect(0, 0, aMaxWidth, aMaxHeight), PaintBox1.Canvas.Brush.Handle);

  if FImage = nil then
    exit;

  Ratio := FImage.GetWidth / FImage.GetHeight;

  Height := FImage.GetHeight;
  Width := FImage.GetWidth;

  if (FImage.GetHeight <= aMaxHeight) and (FImage.GetWidth <= aMaxWidth) then
    { do nothing }; // Width and height allready set

  if (FImage.GetHeight <= aMaxHeight) and (FImage.GetWidth > aMaxWidth) then
    Width := Round(FImage.GetHeight * Ratio);

  if (FImage.GetHeight > aMaxHeight) and (FImage.GetWidth > aMaxWidth) then
    if Ratio > 1 then
    begin
      Height := Round(aMaxWidth / Ratio);
      Width := aMaxWidth;
    end
    else
    begin
      Width := Round(aMaxHeight * Ratio);
      Height := aMaxHeight;
    end;

  HeightOffset := (aMaxHeight - Height) div 2;
  WidthOffset := (aMaxWidth - Width) div 2;

  with TGPGraphics.Create(PaintBox1.Canvas.Handle) do
    try
      // RotateTransform(30);
      DrawImage(FImage, WidthOffset, HeightOffset, Width, Height);
    finally
      Free;
    end;
end;

procedure TForm1.DrawImage;
begin
  DrawImage(PaintBox1.Width, PaintBox1.Height);
end;

procedure TForm1.PaintBox1DblClick(Sender: TObject);
begin
  if not OpenPictureDialog1.Execute then
    exit;
  FImage.Free;
  FImage := TGPImage.Create(OpenPictureDialog1.FileName);
  DrawImage;
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
  DrawImage;
end;

end.

如果我不旋转我的图像一切正常,但是当我调用RotateTransform(30); 时,图像偏离中心。

我已尝试移动原点:

  with TGPGraphics.Create(PaintBox1.Canvas.Handle) do
    try
      TranslateTransform(WidthOffset + (Width div 2), HeightOffset + (Height div 2));
      RotateTransform(30);
      DrawImage(FImage, WidthOffset, HeightOffset, Width, Height);
    finally
      Free;
    end;

但随后图像不可见。

结果

使用下面 MBo 的答案,我得到了这个结果:

procedure TForm1.DrawImage(aMaxWidth, aMaxHeight: Cardinal; aRotationAngle: Double);
var
  radAngle, Ratio: Double;
  Height, Width: Cardinal;
  Wanted_CX, Wanted_CY: Integer;
  WCX_InRotated, WCY_InRotated, xr, yr: Single;
begin
  PaintBox1.Canvas.Brush.Color := clWhite;
  FillRect(PaintBox1.Canvas.Handle, Rect(0, 0, aMaxWidth, aMaxHeight), PaintBox1.Canvas.Brush.Handle);

  if FImage = nil then
    exit;

  Ratio := FImage.GetWidth / FImage.GetHeight;

  Height := FImage.GetHeight;
  Width := FImage.GetWidth;

  if (FImage.GetHeight <= aMaxHeight) and (FImage.GetWidth <= aMaxWidth) then
    { do nothing }; // Width and height allready set

  if (FImage.GetHeight <= aMaxHeight) and (FImage.GetWidth > aMaxWidth) then
    Width := Round(FImage.GetHeight * Ratio);

  if (FImage.GetHeight > aMaxHeight) and (FImage.GetWidth > aMaxWidth) then
    if Ratio > 1 then
    begin
      Height := Round(aMaxWidth / Ratio);
      Width := aMaxWidth;
    end
    else
    begin
      Width := Round(aMaxHeight * Ratio);
      Height := aMaxHeight;
    end;

  radAngle := DegToRad(aRotationAngle);
  Wanted_CX := PaintBox1.Width div 2;
  Wanted_CY := PaintBox1.Height div 2;
  xr := 0.5 * (Width * Cos(radAngle) - Height * Sin(radAngle)); // shift of rotated center
  yr := 0.5 * (Width * Sin(radAngle) + Height * Cos(radAngle)); // relative to left top corner

  with TGPGraphics.Create(PaintBox1.Canvas.Handle) do
    try
      RotateTransform(aRotationAngle);
      WCX_InRotated := Cos(radAngle) * (Wanted_CX - xr) + Sin(radAngle) * (Wanted_CY - yr);
      WCY_InRotated := -Sin(radAngle) * (Wanted_CX - xr) + Cos(radAngle) * (Wanted_CY - yr);
      DrawImage(FImage, WCX_InRotated, WCY_InRotated);
    finally
      Free;
    end;
end;

【问题讨论】:

  • @NGLN 提出并回答了这个问题(+其他一些旋转技术)here

标签: delphi gdi+


【解决方案1】:

问题根源:DrawImage工作在新的(旋转的)坐标系下,在想要的地方画图不是那么简单。

简单示例旋转图片并以给定点为中心输出它(由黄色圆圈显示)。

var
  FImage: TGPImage;
  w, h, Wanted_CX, Wanted_CY: Integer;
  WCX_InRotated, WCY_InRotated, xr, yr: Single;
  Fi, FiRad: Double;
begin
  FImage := TGPImage.Create('d:\distr\pics\test.bmp'); //220x250
  Fi := 30;
  FiRad := DegToRad(Fi);
  w := FImage.GetWidth;
  h := FImage.GetHeight;
  Wanted_CX := 200;     //position of rotated image center
  Wanted_CY := 200;
  xr := 0.5 * (w * Cos(FiRad) - h * Sin(FiRad)); //shift of rotated center
  yr := 0.5 * (w * Sin(FiRad) + h * Cos(FiRad)); //relative to left top corner
  with TGPGraphics.Create(Canvas.Handle) do
    try
      RotateTransform(Fi); //rotates about left top corner
      //transform windows coordinates into rotated system
      WCX_InRotated := Cos(FiRad) * (Wanted_CX - xr) + Sin(FiRad) * (Wanted_CY  - yr);
      WCY_InRotated := -Sin(FiRad) * (Wanted_CX - xr) + Cos(FiRad) * (Wanted_CY  - yr);
      DrawImage(FImage, WCX_InRotated, WCY_InRotated);
    finally
      Free;
    end;

  Canvas.Brush.Color := clYellow;
  Canvas.Ellipse(200 - 4, 200 - 4, 200 + 5, 200 + 5);
end;

【讨论】:

  • 非常感谢。只要您不调整 PaintBox 的大小,您的代码就可以工作。但我相信我能找到一个解决办法,否则我会问一个新问题:D
  • 我没有看到调整 PaintBox 大小的问题 - 您已经考虑了它的尺寸。但是为什么不使用位图来存储PaintBox的图片(计算一次,反复使用)?
  • 老实说:自从我接受你的回答后,我就没有看过它。我只是在调整表单大小时看到了一些问题。我会做一些双重缓冲。
猜你喜欢
  • 1970-01-01
  • 2014-07-11
  • 1970-01-01
  • 2011-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-26
相关资源
最近更新 更多