以前总是用大段大段的cos/sin来写旋转图像的代码,而且旋转出来还经常因为插值的问题导致图像出错。后来稍微研究了一下GDI+,发现直接用GDI+来旋转效果比我自己写好太多了,网上搜到了关于旋转的代码,在这里和大家分享。
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <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)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
if(image == null)
throw new ArgumentNullException("image");
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
const double pi2 = Math.PI / 2.0;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// 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;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// Ensure theta is now [0, 2pi)
while( locked_theta < 0.0 )
locked_theta += 2 * Math.PI;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
double newWidth, newHeight;
int nWidth, nHeight; // The newWidth/newHeight expressed as ints
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
Explaination of the calculations#region Explaination of the calculations
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//*
* 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
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
double adjacentTop, oppositeTop;
double adjacentBottom, oppositeBottom;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// 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) ) )
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
adjacentTop = Math.Abs(Math.Cos(locked_theta)) * oldWidth;
oppositeTop = Math.Abs(Math.Sin(locked_theta)) * oldWidth;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
adjacentBottom = Math.Abs(Math.Cos(locked_theta)) * oldHeight;
oppositeBottom = Math.Abs(Math.Sin(locked_theta)) * oldHeight;
}
else
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
adjacentTop = Math.Abs(Math.Sin(locked_theta)) * oldHeight;
oppositeTop = Math.Abs(Math.Cos(locked_theta)) * oldHeight;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
adjacentBottom = Math.Abs(Math.Sin(locked_theta)) * oldWidth;
oppositeBottom = Math.Abs(Math.Cos(locked_theta)) * oldWidth;
}
newWidth = adjacentTop + oppositeBottom;
newHeight = adjacentBottom + oppositeTop;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
nWidth = (int) Math.Ceiling(newWidth);
nHeight = (int) Math.Ceiling(newHeight);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Bitmap rotatedBmp = new Bitmap(nWidth, nHeight);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
using(Graphics g = Graphics.FromImage(rotatedBmp))
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// This array will be used to pass in the three points that
// make up the rotated image
Point [] points;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//*
* 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 )
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
points = new Point[]
{
new Point( (int) oppositeBottom, 0 ),
new Point( nWidth, (int) oppositeTop ),
new Point( 0, (int) adjacentBottom )
};
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}
else if( locked_theta >= pi2 && locked_theta < Math.PI )
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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) )
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
points = new Point[]
{
new Point( (int) adjacentTop, nHeight ),
new Point( 0, (int) adjacentBottom ),
new Point( nWidth, (int) oppositeTop )
};
}
else
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
points = new Point[]
{
new Point( 0, (int) adjacentBottom ),
new Point( (int) oppositeBottom, 0 ),
new Point( (int) adjacentTop, nHeight )
};
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
g.DrawImage(image, points);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
return rotatedBmp;
}