/// <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, 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
* 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
* Width and height of the original bitmap, I'll associate them
* Based on their position.
*
* Adjacent/oppositetop will refer to the triangles making up
* 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
* 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
// 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
* Fixed locations instead of in relation to
* 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
* 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;
}