在其中心旋转图形位图

Posted

技术标签:

【中文标题】在其中心旋转图形位图【英文标题】:Rotate a Graphics bitmap at its center 【发布时间】:2016-10-20 11:18:18 【问题描述】:

我正在为学校做一个项目,我们需要在不使用 XNA 的情况下用 C# 制作一个基本的自上而下的比赛游戏。 首先让我告诉你,到目前为止,我们学到的关于编程的东西与制作一些看起来像赛车游戏的东西几乎没有关系。它并没有比数组、循环等更难。 所以我们没有学习图形或类似的东西。

说了这么多,我遇到了以下问题。

我们已经创建了一个 Graphics 对象,然后使用 DrawImage 并使用来自 car.jpg 的位图。

graphics = e.Graphics;
graphics.RotateTransform(angle);
graphics.DrawImage(car, xPos, yPos, car.Width, car.Height);

然后我们等待按键,例如 Right

case Keys.Right:
  if (angle != 360)
  
    angle += 10;
  
  else
  
    angle = 0;
  
  this.Refresh();
  break;

我们遇到的问题是旋转的枢轴点在左上角。因此,一旦我们将汽车移动到类似(20,25) 的位置并开始旋转它,它将使用(0,0) 作为旋转中心。我们想要实现的是让旋转中心点位于我们汽车的中心。

我们已尝试寻找更改RotateTransformcenterXcenterY 的方法,但得出的结论是位图无法做到这一点。 我们已经在这个问题上苦苦挣扎了 2 天多,似乎找不到任何解决方案来实现我们想要的东西。

是我们在创建 Graphics 对象时做错了什么,还是有完全不同的方法来更改汽车的 centerXcenterY

【问题讨论】:

真的很简单if你知道诀窍:你需要先移动旋转的中心点作为Graphics对象的原点,旋转,移动它回来了,现在绘制并最终重置。看这里的例子:My answer, not the accepted one! @TaW 我尝试实施您的解决方案并且它有效,但仅适用于第一个位置,只要汽车向任何方向移动,它就会在初始点旋转而不是将原点放置在汽车的新中心。 pastebin.com/59XixX7F 您需要将新位置添加到两个翻译的中点:float bw2 = bmp.Width / 2f + xPos; 等。 【参考方案1】:

要绘制旋转的Bitmap,您需要执行几个步骤来准备Graphics 对象:

首先将其原点移动到旋转的中点 然后按所需的角度旋转 接下来将其移回 现在您可以绘制Bitmap 最后你重置了Graphics

这需要为每个位图完成。

以下是在位置 (xPos, yPos) 处绘制 Bitmap bmp 的代码步骤:

float moveX = bmp.Width / 2f + xPos;   
float moveY = bmp.Height / 2f+ xPosf;   
e.Graphics.TranslateTransform(moveX , moveY );
e.Graphics.RotateTransform(angle);
e.Graphics.TranslateTransform(-moveX , -moveY );
e.Graphics.DrawImage(bmp, xPos, yPos);  
e.Graphics.ResetTransform();

有一种可能的复杂情况:如果您的Bitmapdpi 分辨率与屏幕(即Graphics)不同,您必须首先调整Bitmapdpi 设置!

要使Bitmap 适应通常的96dpi,您可以简单地做一个

bmp.SetResolution(96,96);

要为未来类似视网膜的显示器做好准备,您可以创建一个在启动时设置的类变量:

int ScreenDpi = 96;
private void Form1_Load(object sender, EventArgs e)

  using (Graphics G = this.CreateGraphics()) ScreenDpi = (int)G.DpiX;

并在加载Bitmap后使用它:

bmp.SetResolution(ScreenDpi , ScreenDpi );

像往常一样,DrawImage 方法使用Bitmap 的左上角。您可能需要使用不同的Points 作为旋转点,也可能需要使用汽车的虚拟位置,可能在前部的中间。

【讨论】:

【参考方案2】:

这是一个静态类,它将在所需区域内的所需位置绘制图像。更改旋转角度值以旋转图像。您还可以平移和缩放图像。

在你的项目中添加这个类,并从 Win Form 中调用静态函数。

public static class FullImage


    public static Image image;  
    public static RectangleF DisplayRect, SourceRect;
    public static Size ParentBoundry;
    public static float rotationangle=0;

    internal static void Paint(Graphics graphics)
    
        if (image == null)
            return;
        float hw = DisplayRect.X + DisplayRect.Width / 2f;
        float hh = DisplayRect.Y + DisplayRect.Height / 2f;

        System.Drawing.Drawing2D.Matrix m = graphics.Transform;
        m.RotateAt(rotationangle, new PointF(hw, hh), System.Drawing.Drawing2D.MatrixOrder.Append);
        graphics.Transform = m;
        graphics.DrawImage(image, new RectangleF(DisplayRect.X, DisplayRect.Y, DisplayRect.Width, DisplayRect.Height), SourceRect, GraphicsUnit.Pixel);
        graphics.ResetTransform();
    


    public static void LoadImage(Image img)
             
        image = img;
        SizeF s = GetResizedSize(image, ParentBoundry);
        SourceRect = new RectangleF(0, 0, image.Width, image.Height);
        DisplayRect =  new RectangleF(ParentBoundry.Width / 2 - s.Width / 2, ParentBoundry.Height / 2 - s.Height / 2, s.Width, s.Height);
    

    public static Size GetResizedSize(Image ImageToResize, Size size)
    
        int sourceWidth = ImageToResize.Width;
        int sourceHeight = ImageToResize.Height;

        float nPercent = 0;
        float nPercentW = 0;
        float nPercentH = 0;

        nPercentW = ((float)size.Width / (float)sourceWidth);
        nPercentH = ((float)size.Height / (float)sourceHeight);

        if (nPercentH < nPercentW)
            nPercent = nPercentH;
        else
            nPercent = nPercentW;

        int destWidth = (int)(sourceWidth * nPercent);
        int destHeight = (int)(sourceHeight * nPercent);

        return new Size(destWidth, destHeight);
    

    internal static void MouseWheel(int delta)
    

        if (delta > 0)
            DisplayRect = ZoomImage(DisplayRect,CurrentMouse, .1f);
        else
            DisplayRect = ZoomImage(DisplayRect, CurrentMouse, -.1f);

    

private RectangleF ZoomImage(RectangleF ImageRectangle, PointF MouseLocation, float ScaleFactor)
    

        /// Original Size and Location
        SizeF OriginalSize = ImageRectangle.Size;
        PointF OriginalPoint = ImageRectangle.Location;

        ///Mouse cursor location -located in width% and height% of totaloriginal image
        float mouse_widthpercent = System.Math.Abs(OriginalPoint.X - MouseLocation.X) / OriginalSize.Width * 100;
        float mouse_heightpercent = System.Math.Abs(OriginalPoint.Y - MouseLocation.Y) / OriginalSize.Height * 100;

        ///Zoomed Image by scalefactor
        SizeF FinalSize = new SizeF(OriginalSize.Width + OriginalSize.Width * ScaleFactor, OriginalSize.Height + OriginalSize.Height * ScaleFactor);
        if (FinalSize.Width < 15 || FinalSize.Height < 15)
            return ImageRectangle;
        if (FinalSize.Width > 60000 || FinalSize.Height > 60000)
            return ImageRectangle;
        /// How much width increases and height increases
        float widhtincrease = FinalSize.Width - OriginalSize.Width;
        float heightincrease = FinalSize.Height - OriginalSize.Height;

        /// Adjusting Image location after zooming the image
        PointF FinalLocation = new System.Drawing.PointF(OriginalPoint.X - widhtincrease * mouse_widthpercent / 100,
              OriginalPoint.Y - heightincrease * mouse_heightpercent / 100);
        ImageRectangle = new RectangleF(FinalLocation.X, FinalLocation.Y, FinalSize.Width, FinalSize.Height);

        return ImageRectangle;
    

    static bool drag = false;
    static Point Initial, CurrentMouse;  

    internal static void MouseMove(Point location)
              
        CurrentMouse = location;
        if (drag)
        
            DisplayRect = new RectangleF(DisplayRect.X + location.X - Initial.X, DisplayRect.Y + location.Y - Initial.Y, DisplayRect.Width, DisplayRect.Height);
            Initial = location;
        

    
    internal static void MouseDown(Point location)
           
        Initial = location;
        drag = true;
    

    internal static void MouseUp(Point location)
    
        drag = false;
            


在您的项目中添加此代码后(最好在单独的cs文件中添加),从Win Form类(Form1.cs)中调用函数。

public partial class Form1 : Form

    public Form1()
    
        InitializeComponent();
        this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        this.SetStyle(ControlStyles.ResizeRedraw, true);
        FullImage.ParentBoundry = new Size(this.Width, this.Height);
        // Enter the image path  
        FullImage.LoadImage(Image.FromFile(@"D:\a.jpg"));
    

    //Create a paint event
    private void Form1_Paint(object sender, PaintEventArgs e)
    
        FullImage.Paint(e.Graphics);
    

    private void Form1_MouseDown(object sender, MouseEventArgs e)
    
        Vault.FullImage.MouseDown(e.Location);
        this.Invalidate();
    

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    
        Vault.FullImage.MouseMove(e.Location);
        this.Invalidate();
    

    private void Form1_MouseUp(object sender, MouseEventArgs e)
    
        Vault.FullImage.MouseUp(e.Location);
        this.Invalidate();
    
    protected override void OnMouseWheel(MouseEventArgs e)
    
        Vault.FullImage.MouseWheel(e.Delta);
        this.Invalidate();
    

现在,如果你想旋转图像,只需设置你想要的值(使用滑块、按钮或添加更多功能来检测鼠标移动然后旋转)

示例:添加一个按钮,每次单击该按钮时值增加 1。

  private void button1_clicked(object sender, EventArgs e)
  
       FullImage.rotationangle++;
       this.invalidate();
  

【讨论】:

【参考方案3】:

要从中心旋转左上角,您首先需要知道它的角度,然后按您想要的角度调整它,并通过新角度重新计算新的左上角:

var newXPos = (int)(xPos + car.Width / 2.0 + Math.Cos(Math.Atan2(-car.Height / 2, -car.Width / 2)
    + angle / 180.0 * Math.PI) * -car.Width / 2.0);
var newYPos = (int)(yPos + car.Height / 2.0 + Math.Sin(Math.Atan2(-car.Height / 2, -car.Width / 2)
    + angle / 180.0 * Math.PI) * -car.Height / 2.0);
graphics = e.Graphics;
graphics.RotateTransform(angle);
graphics.DrawImage(car, newXPos, newYPos, car.Width, car.Height);

【讨论】:

以上是关于在其中心旋转图形位图的主要内容,如果未能解决你的问题,请参考以下文章

在其中心旋转方形 TBitmap

在保持区域的同时旋转位图(矩形)

围绕其中心旋转 UIImageView

如何计算围绕其中心旋转的矩形的边界框?

如何使用 Pygame 围绕其中心旋转图像?

当围绕其中心旋转图像时,为啥需要添加/减去质心?