C# GDI+旋转图片(转)

        /// <summary>
        /// Creates a new Image containing the same image only rotated
        /// </summary>
        /// <param name="image">The <see cref="System.Drawing.Image"/> to rotate</param>
        /// <param name="angle">The amount to rotate the image, clockwise, in degrees</param>
        /// <returns>A new <see cref="System.Drawing.Bitmap"/> that is just large enough
        /// to contain the rotated image without cutting any corners off.</returns>
        /// <exception cref="System.ArgumentNullException">Thrown if <see cref="image"/> is null.</exception>
        public static Bitmap RotateImage(Image image, float angle)
        {
            if(image == null)
                throw new ArgumentNullException("image");

            const double pi2 = Math.PI / 2.0;

            // Why can't C# allow these to be const, or at least readonly
            // *sigh*  I'm starting to talk like Christian Graus :omg:
            double oldWidth = (double) image.Width;
            double oldHeight = (double) image.Height;
           
            // Convert degrees to radians
            double theta = ((double) angle) * Math.PI / 180.0;
            double locked_theta = theta;

            // Ensure theta is now [0, 2pi)
            while( locked_theta < 0.0 )
                locked_theta += 2 * Math.PI;

            double newWidth, newHeight;
            int nWidth, nHeight; // The newWidth/newHeight expressed as ints


            #region Explaination of the calculations
            /**//*
             * The trig involved in calculating the new width and height
             * is fairly simple; the hard part was remembering that when
             * PI/2 <= theta <= PI and 3PI/2 <= theta < 2PI the width and
             * height are switched.
             *
             * When you rotate a rectangle, r, the bounding box surrounding r
             * contains for right-triangles of empty space.  Each of the
             * triangles hypotenuse's are a known length, either the width or
             * the height of r.  Because we know the length of the hypotenuse
             * and we have a known angle of rotation, we can use the trig
             * function identities to find the length of the other two sides.
             *
             * sine = opposite/hypotenuse
             * cosine = adjacent/hypotenuse
             *
             * solving for the unknown we get
             *
             * opposite = sine * hypotenuse
             * adjacent = cosine * hypotenuse
             *
             * Another interesting point about these triangles is that there
             * are only two different triangles. The proof for which is easy
             * to see, but its been too long since I've written a proof that
             * I can't explain it well enough to want to publish it. 
             *
             * Just trust me when I say the triangles formed by the lengths
             * width are always the same (for a given theta) and the same
             * goes for the height of r.
             *
             * Rather than associate the opposite/adjacent sides with the
             * width and height of the original bitmap, I'll associate them
             * based on their position.
             *
             * adjacent/oppositeTop will refer to the triangles making up the
             * upper right and lower left corners
             *
             * adjacent/oppositeBottom will refer to the triangles making up
             * the upper left and lower right corners
             *
             * The names are based on the right side corners, because thats
             * where I did my work on paper (the right side).
             *
             * Now if you draw this out, you will see that the width of the
             * bounding box is calculated by adding together adjacentTop and
             * oppositeBottom while the height is calculate by adding
             * together adjacentBottom and oppositeTop.
             */
            #endregion

            double adjacentTop, oppositeTop;
            double adjacentBottom, oppositeBottom;

            // We need to calculate the sides of the triangles based
            // on how much rotation is being done to the bitmap.
            //   Refer to the first paragraph in the explaination above for
            //   reasons why.
            if( (locked_theta >= 0.0 && locked_theta < pi2) ||
                (locked_theta >= Math.PI && locked_theta < (Math.PI + pi2) ) )
            {
                adjacentTop = Math.Abs(Math.Cos(locked_theta)) * oldWidth;
                oppositeTop = Math.Abs(Math.Sin(locked_theta)) * oldWidth;

                adjacentBottom = Math.Abs(Math.Cos(locked_theta)) * oldHeight;
                oppositeBottom = Math.Abs(Math.Sin(locked_theta)) * oldHeight;
            }
            else
            {
                adjacentTop = Math.Abs(Math.Sin(locked_theta)) * oldHeight;
                oppositeTop = Math.Abs(Math.Cos(locked_theta)) * oldHeight;

                adjacentBottom = Math.Abs(Math.Sin(locked_theta)) * oldWidth;
                oppositeBottom = Math.Abs(Math.Cos(locked_theta)) * oldWidth;
            }
           
            newWidth = adjacentTop + oppositeBottom;
            newHeight = adjacentBottom + oppositeTop;

            nWidth = (int) Math.Ceiling(newWidth);
            nHeight = (int) Math.Ceiling(newHeight);

            Bitmap rotatedBmp = new Bitmap(nWidth, nHeight);

            using(Graphics g = Graphics.FromImage(rotatedBmp))
            {
                // This array will be used to pass in the three points that
                // make up the rotated image
                Point [] points;

                /**//*
                 * The values of opposite/adjacentTop/Bottom are referring to
                 * fixed locations instead of in relation to the
                 * rotating image so I need to change which values are used
                 * based on the how much the image is rotating.
                 *
                 * For each point, one of the coordinates will always be 0,
                 * nWidth, or nHeight.  This because the Bitmap we are drawing on
                 * is the bounding box for the rotated bitmap.  If both of the
                 * corrdinates for any of the given points wasn't in the set above
                 * then the bitmap we are drawing on WOULDN'T be the bounding box
                 * as required.
                 */
                if( locked_theta >= 0.0 && locked_theta < pi2 )
                {
                    points = new Point[] {
                                             new Point( (int) oppositeBottom, 0 ),
                                             new Point( nWidth, (int) oppositeTop ),
                                             new Point( 0, (int) adjacentBottom )
                                         };

                }
                else if( locked_theta >= pi2 && locked_theta < Math.PI )
                {
                    points = new Point[] {
                                             new Point( nWidth, (int) oppositeTop ),
                                             new Point( (int) adjacentTop, nHeight ),
                                             new Point( (int) oppositeBottom, 0 )                        
                                         };
                }
                else if( locked_theta >= Math.PI && locked_theta < (Math.PI + pi2) )
                {
                    points = new Point[] {
                                             new Point( (int) adjacentTop, nHeight ),
                                             new Point( 0, (int) adjacentBottom ),
                                             new Point( nWidth, (int) oppositeTop )
                                         };
                }
                else
                {
                    points = new Point[] {
                                             new Point( 0, (int) adjacentBottom ),
                                             new Point( (int) oppositeBottom, 0 ),
                                             new Point( (int) adjacentTop, nHeight )       
                                         };
                }

                g.DrawImage(image, points);
            }

            return rotatedBmp;
        }

posted @ 2010-05-25 15:40  哭过的天空  阅读(3291)  评论(0编辑  收藏  举报