[.net] Rotating a Bitmap in C#

Started by
1 comment, last by TheTroll 14 years, 1 month ago
Hello fellow Gamedevs. I'm preparing a basic engine setup for my game project that I'm going to do in C#. This is an assignment for university, and we've been instructed to not use APIs such as OpenGL and DirectX. I've made a basic image rendering class which is almost complete, I just haven't fully completed the method for rotating a Bitmap. Personally, I don't feel like I'm 100% educated in terms of rotation logic and such. What I'm hoping is that the reponses to this, code samples or not, will help shed some light on the issue at hand. I'm aware of how rotation works for matrices and such, just not how to properly apply them to a bitmap. The rotation function that I've used is courtesy of vcskicks.com.

       private System.Drawing.Image RotateImage(System.Drawing.Image inputImg, double degreeAngle)
        {
            //Corners of the image
            PointF[] rotationPoints = { new PointF(0, 0),
                                        new PointF(inputImg.Width, 0),
                                        new PointF(0, inputImg.Height),
                                        new PointF(inputImg.Width, inputImg.Height)};

            //Rotate the corners
            PointMath.RotatePoints(rotationPoints, new PointF(inputImg.Width / 2.0f, inputImg.Height / 2.0f), degreeAngle);

            //Get the new bounds given from the rotation of the corners
            //(avoid clipping of the image)
            Rectangle bounds = PointMath.GetBounds(rotationPoints);

            //An empy bitmap to draw the rotated image
            Bitmap rotatedBitmap = new Bitmap(bounds.Width, bounds.Height);

            using (Graphics g = Graphics.FromImage(rotatedBitmap))
            {
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;

                //Transformation matrix
                Matrix m = new Matrix();
                m.RotateAt((float)degreeAngle, new PointF(inputImg.Width / 2.0f, inputImg.Height / 2.0f));
                m.Translate(-bounds.Left, -bounds.Top, MatrixOrder.Append); //shift to compensate for the rotation

                g.Transform = m;
                g.Clear(Color.White);
                g.DrawImage(inputImg, 0, 0);
            }
            return (System.Drawing.Image)rotatedBitmap;
        }




    public static class PointMath
    {
        private static double DegreeToRadian(double angle)
        {
            return Math.PI * angle / 180.0;
        }

        public static PointF RotatePoint(PointF pnt, double degreeAngle)
        {
            return RotatePoint(pnt, new PointF(0, 0), degreeAngle);
        }

        public static PointF RotatePoint(PointF pnt, PointF origin, double degreeAngle)
        {
            double radAngle = DegreeToRadian(degreeAngle);

            PointF newPoint = new PointF();

            double deltaX = pnt.X - origin.X;
            double deltaY = pnt.Y - origin.Y;

            newPoint.X = (float)(origin.X + (Math.Cos(radAngle) * deltaX - Math.Sin(radAngle) * deltaY));
            newPoint.Y = (float)(origin.Y + (Math.Sin(radAngle) * deltaX + Math.Cos(radAngle) * deltaY));

            return newPoint;
        }

        public static void RotatePoints(PointF[] pnts, double degreeAngle)
        {
            for (int i = 0; i < pnts.Length; i++)
            {
                pnts = RotatePoint(pnts, degreeAngle);
            }
        }

        public static void RotatePoints(PointF[] pnts, PointF origin, double degreeAngle)
        {
            for (int i = 0; i < pnts.Length; i++)
            {
                pnts = RotatePoint(pnts, origin, degreeAngle);
            }
        }

        public static Rectangle GetBounds(PointF[] pnts)
        {
            RectangleF boundsF = GetBoundsF(pnts);
            return new Rectangle((int)Math.Round(boundsF.Left),
                                 (int)Math.Round(boundsF.Top),
                                 (int)Math.Round(boundsF.Width),
                                 (int)Math.Round(boundsF.Height));
        }

        public static RectangleF GetBoundsF(PointF[] pnts)
        {
            float left = pnts[0].X;
            float right = pnts[0].X;
            float top = pnts[0].Y;
            float bottom = pnts[0].Y;

            for (int i = 1; i < pnts.Length; i++)
            {
                if (pnts.X < left)
                    left = pnts.X;
                else if (pnts.X > right)
                    right = pnts.X;

                if (pnts.Y < top)
                    top = pnts.Y;
                else if (pnts.Y > bottom)
                    bottom = pnts.Y;
            }

            return new RectangleF(left,
                                  top,
                                 (float)Math.Abs(right - left),
                                 (float)Math.Abs(bottom - top));
        }
    }

This produces the following result: 0 degrees: http://i47.tinypic.com/r8cxhd.png 22.5 degrees: http://i46.tinypic.com/hsoxo9.png 45 degrees: http://i50.tinypic.com/vy6q8z.png From what I can see, the produced Bitmap only extends its boundaries towards the right and the bottom, while it should also extend in the other 2 directions to make the image rotate properly around the center. I spent quite a while playing around with the method, and in the end I was unable to produce the desired result. The background of the produced Bitmap is colored white simply to be able to see its real size. Any form of assistance is gladly appreciated. :)
Advertisement
Oh wow, just after posting this, I realized that I had made the stupid mistake of always drawing the top-left corner at a specific x,y position. This entire issue is fixed if I specify the location of the middle pixel, and then draw the top-left corner at mid_x - width/2, mid_y - height/2.

I feel kind of silly now. :(

This topic became useless pretty quick... I suppose if anyone has any tips in terms of this kind of coding practice and optimization around Bitmap transformations, I'll happily accept them.
This is why they make XNA, you might want to look into it.

theTroll

This topic is closed to new replies.

Advertisement