【问题标题】:How do I rotate a picture in WinForms如何在 WinForms 中旋转图片
【发布时间】:2011-01-10 22:52:16
【问题描述】:

我想在我的应用程序中有一张图片,我可以旋转以指示方向,例如风向。甚至时间。我用什么代码来旋转图片?谢谢

更新:我正在使用 .NET 2.0、Windows 2000、VS C# 2005

【问题讨论】:

  • 您使用的是 WinForms 还是 WPF?
  • 不是 WPF,我不认为是 winforms。
  • 如果您不使用 WPF(带有 .xaml 文件等)并且您正在 Visual Studio 中使用 C# 开发 UI,那么您正在使用 WinForms 进行开发。它是 Windows 窗体的缩写。

标签: c# winforms image


【解决方案1】:

这是一个可以用来在 C# 中旋转图像的方法:

/// <summary>
/// method to rotate an image either clockwise or counter-clockwise
/// </summary>
/// <param name="img">the image to be rotated</param>
/// <param name="rotationAngle">the angle (in degrees).
/// NOTE: 
/// Positive values will rotate clockwise
/// negative values will rotate counter-clockwise
/// </param>
/// <returns></returns>
public static Image RotateImage(Image img, float rotationAngle)
{
    //create an empty Bitmap image
    Bitmap bmp = new Bitmap(img.Width, img.Height);

    //turn the Bitmap into a Graphics object
    Graphics gfx = Graphics.FromImage(bmp);

    //now we set the rotation point to the center of our image
    gfx.TranslateTransform((float)bmp.Width / 2, (float)bmp.Height / 2);

    //now rotate the image
    gfx.RotateTransform(rotationAngle);

    gfx.TranslateTransform(-(float)bmp.Width / 2, -(float)bmp.Height / 2);

    //set the InterpolationMode to HighQualityBicubic so to ensure a high
    //quality image once it is transformed to the specified size
    gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;

    //now draw our new image onto the graphics object
    gfx.DrawImage(img, new Point(0, 0));

    //dispose of our Graphics object
    gfx.Dispose();

    //return the image
    return bmp;
}

【讨论】:

  • 不错,紧凑的实现。
  • 旋转后的图像可能比原始图像大,如果你在 0 到 90 度之间旋转,比如 45 度,它会流过边缘,你必须为此腾出空间。
  • @Orhan Cinar 在创建新 bmp 的正下方添加以下行。 bmp.SetResolution(img.Horizo​​ntalResolution, img.VerticalResolution);该行会将新的图像分辨率设置为原始图像。
  • 试过代码,不知怎的,我的图像变大了。这是一个完美圆圈内的箭头。箭头指向右侧,当我单击按钮时,我将其旋转 180 度以指向左侧,但图像更大
  • 别忘了在你的项目中添加对 System.Drawing 的引用
【解决方案2】:

这是一个旧线程,还有其他几个关于 C# WinForms 图像旋转的线程,但现在我已经提出了我的解决方案,我认为这是一个发布它的好地方。

  /// <summary>
  /// Method to rotate an Image object. The result can be one of three cases:
  /// - upsizeOk = true: output image will be larger than the input, and no clipping occurs 
  /// - upsizeOk = false & clipOk = true: output same size as input, clipping occurs
  /// - upsizeOk = false & clipOk = false: output same size as input, image reduced, no clipping
  /// 
  /// A background color must be specified, and this color will fill the edges that are not 
  /// occupied by the rotated image. If color = transparent the output image will be 32-bit, 
  /// otherwise the output image will be 24-bit.
  /// 
  /// Note that this method always returns a new Bitmap object, even if rotation is zero - in 
  /// which case the returned object is a clone of the input object. 
  /// </summary>
  /// <param name="inputImage">input Image object, is not modified</param>
  /// <param name="angleDegrees">angle of rotation, in degrees</param>
  /// <param name="upsizeOk">see comments above</param>
  /// <param name="clipOk">see comments above, not used if upsizeOk = true</param>
  /// <param name="backgroundColor">color to fill exposed parts of the background</param>
  /// <returns>new Bitmap object, may be larger than input image</returns>
  public static Bitmap RotateImage(Image inputImage, float angleDegrees, bool upsizeOk, 
                                   bool clipOk, Color backgroundColor)
  {
     // Test for zero rotation and return a clone of the input image
     if (angleDegrees == 0f)
        return (Bitmap)inputImage.Clone();

     // Set up old and new image dimensions, assuming upsizing not wanted and clipping OK
     int oldWidth = inputImage.Width;
     int oldHeight = inputImage.Height;
     int newWidth = oldWidth;
     int newHeight = oldHeight;
     float scaleFactor = 1f;

     // If upsizing wanted or clipping not OK calculate the size of the resulting bitmap
     if (upsizeOk || !clipOk)
     {
        double angleRadians = angleDegrees * Math.PI / 180d;

        double cos = Math.Abs(Math.Cos(angleRadians));
        double sin = Math.Abs(Math.Sin(angleRadians));
        newWidth = (int)Math.Round(oldWidth * cos + oldHeight * sin);
        newHeight = (int)Math.Round(oldWidth * sin + oldHeight * cos);
     }

     // If upsizing not wanted and clipping not OK need a scaling factor
     if (!upsizeOk && !clipOk)
     {
        scaleFactor = Math.Min((float)oldWidth / newWidth, (float)oldHeight / newHeight);
        newWidth = oldWidth;
        newHeight = oldHeight;
     }

     // Create the new bitmap object. If background color is transparent it must be 32-bit, 
     //  otherwise 24-bit is good enough.
     Bitmap newBitmap = new Bitmap(newWidth, newHeight, backgroundColor == Color.Transparent ? 
                                      PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb);
     newBitmap.SetResolution(inputImage.HorizontalResolution, inputImage.VerticalResolution);

     // Create the Graphics object that does the work
     using (Graphics graphicsObject = Graphics.FromImage(newBitmap))
     {
        graphicsObject.InterpolationMode = InterpolationMode.HighQualityBicubic;
        graphicsObject.PixelOffsetMode = PixelOffsetMode.HighQuality;
        graphicsObject.SmoothingMode = SmoothingMode.HighQuality;

        // Fill in the specified background color if necessary
        if (backgroundColor != Color.Transparent)
           graphicsObject.Clear(backgroundColor);

        // Set up the built-in transformation matrix to do the rotation and maybe scaling
        graphicsObject.TranslateTransform(newWidth / 2f, newHeight / 2f);

        if (scaleFactor != 1f)
           graphicsObject.ScaleTransform(scaleFactor, scaleFactor);

        graphicsObject.RotateTransform(angleDegrees);
        graphicsObject.TranslateTransform(-oldWidth / 2f, -oldHeight / 2f);

        // Draw the result 
        graphicsObject.DrawImage(inputImage, 0, 0);
     }

     return newBitmap;
  }

这是来自 StackOverflow 和其他地方的许多灵感来源的结果。 Naveen 在this thread 上的回答特别有帮助。

【讨论】:

  • 我一直在寻找的东西,最好的答案!
  • 我也在寻找一个好的 C# 图像旋转解决方案并尝试了这个:虽然它可以正确旋转图像,但它会返回一个周围有很大空白空间的图像。我确实知道旋转的图像不一定具有相同的尺寸,但为什么它周围有一个空边框?不幸的是,这些参数没有帮助:我设置了 clipOk=false 和 upsizeOk=true,然后我得到一个周围有很大空白空间的图像,如果我设置 upsizeOk=false,那么图像具有原始大小,但仍然有一些边框原来没有的地方...有什么想法吗?
  • @ab-tools:您确定您的输入图像在图像的可见部分周围没有多余的空间吗?这是一种可能的解释......
  • 很好的答案,但我在整个互联网上寻找,仍然没有找到我需要的东西:您的解决方案,即使使用upsizeOk=true,在不旋转时也会使我的图像比原始图像更小.. .我在旋转图像时在互联网上找到的所有解决方案,使其变小或剪切它......(我的图像没有空格)。你知道如何避免这个问题吗?
【解决方案3】:

简单方法:

public Image RotateImage(Image img)
{
    var bmp = new Bitmap(img);

    using (Graphics gfx = Graphics.FromImage(bmp))
    {
        gfx.Clear(Color.White);
        gfx.DrawImage(img, 0, 0, img.Width, img.Height);
    }

    bmp.RotateFlip(RotateFlipType.Rotate270FlipNone);
    return bmp;
}

【讨论】:

  • 嗯,为什么要创建Graphics对象并用它来清除和重绘Bitmap对象呢?这不是不必要的吗?没有它,代码似乎可以正常工作,至少在可以使用 Bitmap.RotateFlip() 的情况下。
  • 效果很好!如果要旋转到 90 度,则应使用 bmp.RotateFlip(RotateFlipType.Rotate90FlipNone);
  • 这里有很多额外的努力。我们可以做到这一点,而无需将其膨胀到 Bitmap 并返回到 Imageimg.RotateFlip(RotateFlipType.Rotate270FlipNone);
【解决方案4】:

我找到了这个article

  /// <summary>
    /// Creates a new Image containing the same image only rotated
    /// </summary>
    /// <param name=""image"">The <see cref=""System.Drawing.Image"/"> to rotate
    /// <param name=""offset"">The position to rotate from.
    /// <param name=""angle"">The amount to rotate the image, clockwise, in degrees
    /// <returns>A new <see cref=""System.Drawing.Bitmap"/"> of the same size rotated.</see>
    /// <exception cref=""System.ArgumentNullException"">Thrown if <see cref=""image"/"> 
    /// is null.</see>
    public static Bitmap RotateImage(Image image, PointF offset, float angle)
    {
        if (image == null)
            throw new ArgumentNullException("image");

        //create a new empty bitmap to hold rotated image
        Bitmap rotatedBmp = new Bitmap(image.Width, image.Height);
        rotatedBmp.SetResolution(image.HorizontalResolution, image.VerticalResolution);

        //make a graphics object from the empty bitmap
        Graphics g = Graphics.FromImage(rotatedBmp);

        //Put the rotation point in the center of the image
        g.TranslateTransform(offset.X, offset.Y);

        //rotate the image
        g.RotateTransform(angle);

        //move the image back
        g.TranslateTransform(-offset.X, -offset.Y);

        //draw passed in image onto graphics object
        g.DrawImage(image, new PointF(0, 0));

        return rotatedBmp;
    }

【讨论】:

    【解决方案5】:

    我写了一个简单的旋转图像类。您所要做的就是以度为单位输入图像和旋转角度。角度必须在 -90 到 +90 之间。

    public class ImageRotator
    {
        private readonly Bitmap image;
        public Image OriginalImage
        {
            get { return image; }
        }
    
    
        private ImageRotator(Bitmap image)
        {
            this.image = image;
        }
    
    
        private double GetRadian(double degree)
        {
            return degree * Math.PI / (double)180;
        }
    
    
        private Size CalculateSize(double angle)
        {
            double radAngle = GetRadian(angle);
            int width = (int)(image.Width * Math.Cos(radAngle) + image.Height * Math.Sin(radAngle));
            int height = (int)(image.Height * Math.Cos(radAngle) + image.Width * Math.Sin(radAngle));
            return new Size(width, height);
        }
    
        private PointF GetTopCoordinate(double radAngle)
        {
            Bitmap image = CurrentlyViewedMappedImage.BitmapImage;
            double topX = 0;
            double topY = 0;
    
            if (radAngle > 0)
            {
                topX = image.Height * Math.Sin(radAngle);
            }
            if (radAngle < 0)
            {
                topY = image.Width * Math.Sin(-radAngle);
            }
            return new PointF((float)topX, (float)topY);
        }
    
        public Bitmap RotateImage(double angle)
        {
            SizeF size = CalculateSize(radAngle);
            Bitmap rotatedBmp = new Bitmap((int)size.Width, (int)size.Height);
    
            Graphics g = Graphics.FromImage(rotatedBmp);
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.PixelOffsetMode = PixelOffsetMode.HighQuality;
    
            g.TranslateTransform(topPoint.X, topPoint.Y);
            g.RotateTransform(GetDegree(radAngle));
            g.DrawImage(image, new RectangleF(0, 0, size.Width, size.Height));
    
            g.Dispose();
            return rotatedBmp;
        }
    
    
        public static class Builder
        {
            public static ImageRotator CreateInstance(Image image)
            {
                ImageRotator rotator = new ImageRotator(image as Bitmap);
                return rotator;
            }
        }
    }
    

    【讨论】:

    • 这不会编译 - 有两个未定义的变量和一个未定义的方法。
    【解决方案6】:

    老问题,但我必须在接受的答案中解决 MrFox 的评论。当尺寸改变时旋转图像会切断图像的边缘。一种解决方案是在较大的图像上重绘原始图像,居中,其中较大图像的尺寸补偿了不剪裁边缘的需要。例如,我希望能够以正常角度设计游戏的图块,但以 45 度角重新绘制它们以获得等距视图。

    这里是示例图片(黄色边框是为了方便查看)。

    原图:

    大图中居中的图块:

    旋转的图像(您旋转较大的图像,而不是原始图像):

    代码(部分基于this answer in another question):

    private Bitmap RotateImage(Bitmap rotateMe, float angle)
    {
        //First, re-center the image in a larger image that has a margin/frame
        //to compensate for the rotated image's increased size
    
        var bmp = new Bitmap(rotateMe.Width + (rotateMe.Width / 2), rotateMe.Height + (rotateMe.Height / 2));
    
        using (Graphics g = Graphics.FromImage(bmp))
            g.DrawImageUnscaled(rotateMe, (rotateMe.Width / 4), (rotateMe.Height / 4), bmp.Width, bmp.Height);
    
        bmp.Save("moved.png");
        rotateMe = bmp;
    
        //Now, actually rotate the image
        Bitmap rotatedImage = new Bitmap(rotateMe.Width, rotateMe.Height);
    
        using (Graphics g = Graphics.FromImage(rotatedImage))
        {
            g.TranslateTransform(rotateMe.Width / 2, rotateMe.Height / 2);   //set the rotation point as the center into the matrix
            g.RotateTransform(angle);                                        //rotate
            g.TranslateTransform(-rotateMe.Width / 2, -rotateMe.Height / 2); //restore rotation point into the matrix
            g.DrawImage(rotateMe, new Point(0, 0));                          //draw the image on the new bitmap
        }
    
        rotatedImage.Save("rotated.png");
        return rotatedImage;
    }
    

    【讨论】:

    • 我的代码有一个问题,它在创建新的位图时存在内存泄漏,没有处理它们,所以当我多次转换一张图像时,它会逐渐占用所有内存,并且没有内存时崩溃
    • @Hatik - 为临时位图添加“处置”(或使用)
    【解决方案7】:

    Richard Cox 对我过去使用过的 https://stackoverflow.com/a/5200280/1171321 有一个很好的解决方案。还值得注意的是,DPI 必须为 96 才能正常工作。此页面上的一些解决方案根本不起作用。

    【讨论】:

      【解决方案8】:

      旋转图像是一回事,正确的图像边界是另一回事。这是一个可以帮助任何人的代码。我很久以前根据互联网上的一些搜索创建了这个。

          /// <summary>
          /// Rotates image in radian angle
          /// </summary>
          /// <param name="bmpSrc"></param>
          /// <param name="theta">in radian</param>
          /// <param name="extendedBitmapBackground">Because of rotation returned bitmap can have different boundaries from original bitmap. This color is used for filling extra space in bitmap</param>
          /// <returns></returns>
          public static Bitmap RotateImage(Bitmap bmpSrc, double theta, Color? extendedBitmapBackground = null)
          {
              theta = Convert.ToSingle(theta * 180 / Math.PI);
              Matrix mRotate = new Matrix();
              mRotate.Translate(bmpSrc.Width / -2, bmpSrc.Height / -2, MatrixOrder.Append);
              mRotate.RotateAt((float)theta, new Point(0, 0), MatrixOrder.Append);
              using (GraphicsPath gp = new GraphicsPath())
              {  // transform image points by rotation matrix
                  gp.AddPolygon(new Point[] { new Point(0, 0), new Point(bmpSrc.Width, 0), new Point(0, bmpSrc.Height) });
                  gp.Transform(mRotate);
                  PointF[] pts = gp.PathPoints;
      
                  // create destination bitmap sized to contain rotated source image
                  Rectangle bbox = BoundingBox(bmpSrc, mRotate);
                  Bitmap bmpDest = new Bitmap(bbox.Width, bbox.Height);
      
                  using (Graphics gDest = Graphics.FromImage(bmpDest))
                  {
                      if (extendedBitmapBackground != null)
                      {
                          gDest.Clear(extendedBitmapBackground.Value);
                      }
                      // draw source into dest
                      Matrix mDest = new Matrix();
                      mDest.Translate(bmpDest.Width / 2, bmpDest.Height / 2, MatrixOrder.Append);
                      gDest.Transform = mDest;
                      gDest.DrawImage(bmpSrc, pts);
                      return bmpDest;
                  }
              }
          }
      
      
          private static Rectangle BoundingBox(Image img, Matrix matrix)
          {
              GraphicsUnit gu = new GraphicsUnit();
              Rectangle rImg = Rectangle.Round(img.GetBounds(ref gu));
      
              // Transform the four points of the image, to get the resized bounding box.
              Point topLeft = new Point(rImg.Left, rImg.Top);
              Point topRight = new Point(rImg.Right, rImg.Top);
              Point bottomRight = new Point(rImg.Right, rImg.Bottom);
              Point bottomLeft = new Point(rImg.Left, rImg.Bottom);
              Point[] points = new Point[] { topLeft, topRight, bottomRight, bottomLeft };
              GraphicsPath gp = new GraphicsPath(points, new byte[] { (byte)PathPointType.Start, (byte)PathPointType.Line, (byte)PathPointType.Line, (byte)PathPointType.Line });
              gp.Transform(matrix);
              return Rectangle.Round(gp.GetBounds());
          }
      

      【讨论】:

        【解决方案9】:

        只要您要旋转的图像已经在您的属性资源文件夹中,这将起作用。

        在部分课程中:

        Bitmap bmp2;
        

        加载时:

         bmp2 = new Bitmap(Tycoon.Properties.Resources.save2);
                    pictureBox6.SizeMode = PictureBoxSizeMode.StretchImage;
                    pictureBox6.Image = bmp2;
        

        按钮或点击

        private void pictureBox6_Click(object sender, EventArgs e)
                {
                    if (bmp2 != null)
                    {
                        bmp2.RotateFlip(RotateFlipType.Rotate90FlipNone);
                        pictureBox6.Image = bmp2;
                    }
                }
        

        【讨论】:

          【解决方案10】:

          您可以通过调用此方法轻松完成:

          public static Bitmap RotateImage(Image image, float angle)
          {
              if (image == null)
                  throw new ArgumentNullException("image");
          
              PointF offset = new PointF((float)image.Width / 2, (float)image.Height / 2);
          
              //create a new empty bitmap to hold rotated image
              Bitmap rotatedBmp = new Bitmap(image.Width, image.Height);
              rotatedBmp.SetResolution(image.HorizontalResolution, image.VerticalResolution);
          
              //make a graphics object from the empty bitmap
              Graphics g = Graphics.FromImage(rotatedBmp);
          
              //Put the rotation point in the center of the image
              g.TranslateTransform(offset.X, offset.Y);
          
              //rotate the image
              g.RotateTransform(angle);
          
              //move the image back
              g.TranslateTransform(-offset.X, -offset.Y);
          
              //draw passed in image onto graphics object
              g.DrawImage(image, new PointF(0, 0));
          
              return rotatedBmp;
          }
          

          不要忘记在您的项目中添加对 System.Drawing.dll 的引用

          此方法调用示例:

          Image image = new Bitmap("waves.png");
          Image newImage = RotateImage(image, 360);
          newImage.Save("newWaves.png");
          

          【讨论】:

          • 不要忘记在 using 语句或 using 块中声明 Graphics g 对象,它实现了 IDisposable 接口。
          【解决方案11】:

          我修改了Mr.net_prog的功能,它只获取图片框并旋转其中的图像。

              public static void RotateImage(PictureBox picBox)
              {
          
                  Image img = picBox.Image;
                  var bmp = new Bitmap(img);
          
                  using (Graphics gfx = Graphics.FromImage(bmp))
                  {
                      gfx.Clear(Color.White);
                      gfx.DrawImage(img, 0, 0, img.Width, img.Height);
                  }
          
                  bmp.RotateFlip(RotateFlipType.Rotate270FlipNone);
                  picBox.Image = bmp;
              }
          

          【讨论】:

            【解决方案12】:

            此解决方案假定您要在图片框中绘制图像,并且图像方向将跟随鼠标在此图片框上的移动。没有图像分配给图片框。相反,我从项目资源中获取图像。

            private float _angle;
            
            public Form1()
            {
                InitializeComponent();
            }
            
            private void PictureBox_MouseMove(object sender, MouseEventArgs e)
            {
                (float centerX, float centerY) = GetCenter(pictureBox1.ClientRectangle);
                _angle = (float)(Math.Atan2(e.Y - centerY, e.X - centerX) * 180.0 / Math.PI);
                pictureBox1.Invalidate(); // Triggers redrawing
            }
            
            private void PictureBox_Paint(object sender, PaintEventArgs e)
            {
                Bitmap image = Properties.Resources.ExampleImage;
                float scale = (float)pictureBox1.Width / image.Width;
            
                (float centerX, float centerY) = GetCenter(e.ClipRectangle);
            
                e.Graphics.TranslateTransform(centerX, centerY);
                e.Graphics.RotateTransform(_angle);
                e.Graphics.TranslateTransform(-centerX, -centerY);
                e.Graphics.ScaleTransform(scale, scale);
                e.Graphics.DrawImage(image, 0, 0);
            }
            
            // Uses C# 7.0 value tuples / .NET Framework 4.7.
            // For previous versions, return a PointF instead.
            private static (float, float) GetCenter(Rectangle rect)
            {
                float centerX = (rect.Left + rect.Right) * 0.5f;
                float centerY = (rect.Top + rect.Bottom) * 0.5f;
                return (centerX, centerY);
            }
            

            在您将此代码复制/粘贴到表单后,请确保在图片框的属性窗口中为这些事件选择鼠标事件处理程序PictureBox_MouseMovePictureBox_Paint

            注意:您也可以使用简单的Panel 或任何其他控件,例如标签;但是,PictureBox 的优势在于默认使用双缓冲,从而消除了闪烁。

            【讨论】:

              猜你喜欢
              • 2013-07-14
              • 1970-01-01
              • 2020-12-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多