Add your own alternative version
Stats
301.3K views 14.3K downloads 91 bookmarked
Posted
6 Dec 2002

Comments and Discussions






hi my dear
tanx for source code very nice
roza





Great problem solving analysis.





Change your code for testing for speed perfomance... Set rotate event on trackbar scrolling... Image was 1280x1024 resolution... work very slow! Is it real to make rotate more fast?





Its not code its GDI and thats normal. If you want fast rotation by your Trackbar you have to reduce the drawing quality and when you get required rotation you can draw with good quality.
You can use following code to set the quality to lower and speed will be high.
g.CompositingQuality= CompositingQuality.HighSpeed;
g.SmoothingMode = SmoothingMode.HighSpeed;
TVMU^P[[IGIOQHG^JSH`A#@`RFJ\c^JPL>;"[,*/+&WLEZGc`AFXc!L
%^]*IRXD#@GKCQ`R\^SF_WcHbORY87֦ʻ6ϣN8ȤBcRAV\Z^&SU~%CSWQ@#2
W_AD`EPABIKRDFVS)EVLQK)JKQUFK[M`UKs*$GwU#QDXBER@CBN%
R0~53%eYrd8mt^7Z6]iTF+(EWfJ9zaKiTV.C\y<pjxsgb$f4ia>

128 bit encrypted signature, crack if you can





Any comments or ideas on this are most welcome
Public Shared Function RotateImage(ByVal image As Bitmap, ByVal angle As Single) As Bitmap
Dim matrix As New Matrix()
matrix.Rotate(angle)
Dim bitmap As New Bitmap(image.Width, image.Height)
bitmap.SetResolution(image.HorizontalResolution, image.VerticalResolution)
Dim graphics As Graphics = graphics.FromImage(bitmap)
graphics.Transform = matrix
graphics.DrawImage(image, New PointF(0, 0))
graphics.Dispose()
matrix.Dispose()
Return bitmap
End Function





I have rotated the image but the output image is of low quality
i have tried bicubic method
smothing etc.But still no improvement





Thanks for this excellent Article.
Two additional annotations:
For rotating in 90 degree steps Image.RotateFlip() may be easier to use.
For C# 3.0 you may use a extension method like:
public static Bitmap Rotate(this Image image, float angle)
{
..
}
thanks!
Tobias





How could you add to your program a saving patern. Like beeing able to save as a new file the modification that you have done. Also, a way to automate the whole process ( I have to take an image and save it multiple times but each time with +X degree. Nice software tho really usefull to learn!





Thanks for this, works perfectly.





I have a text line that each word in this line must be rotated with different angles. I calculated the coordinates of four corners of each word before and after rotation but I do not know how to do it for each pixel? which interpolation is the best for this purpose? ( bilinear, nearest neighbour or bicubic)? my image is binary.
I am writing my codes in VC++ v.8 and I am a bit new in this area.
Thank you





When i rotated the image ,i found the image became much more blur.
Could anyone fix it?





I went in and tuned this persons code so now the image rotation is perfect from i can see. Their was some calls to Math.Cieling in original code, and uses of int when float was acceptable, etc that just messed up the image... so after i fixed all that, the code works really perfectly now :P
In my program, i let the user rotate a image by dragging on a progressbar... so i do low quality rotations and when user releases mouse button, it does one high quality... that's just some advice for people that need a performance boost :P
public static Bitmap RotateImage(Image image, double angle, bool highDetail)
{
if (image == null)
throw new ArgumentNullException("image");
const double pi2 = Math.PI / 2.0D;
double oldWidth = (double)image.Width;
double oldHeight = (double)image.Height;
double theta = angle * Math.PI / 180.0D;
double locked_theta = theta;
while (locked_theta < 0.0D)
locked_theta += 2.0D * Math.PI;
double newWidth, newHeight;
int nWidth, nHeight;
#region Explaination of the calculations
#endregion
double adjacentTop, oppositeTop;
double adjacentBottom, oppositeBottom;
if ((locked_theta >= 0.0D && 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)newWidth;
nHeight = (int)newHeight;
Bitmap rotatedBmp = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage(rotatedBmp))
{
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
if (highDetail)
{
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
}
else
{
g.SmoothingMode = SmoothingMode.None;
g.InterpolationMode = InterpolationMode.Low;
}
PointF[] points;
if (locked_theta >= 0.0D && locked_theta < pi2)
{
points = new PointF[] {
new PointF( (float)oppositeBottom, 0.0F ),
new PointF( (float)newWidth, (float)oppositeTop ),
new PointF( 0.0F, (float) adjacentBottom )
};
}
else if (locked_theta >= pi2 && locked_theta < Math.PI)
{
points = new PointF[] {
new PointF( (float)newWidth, (float) oppositeTop ),
new PointF( (float) adjacentTop, (float)newHeight ),
new PointF( (float) oppositeBottom, 0.0F )
};
}
else if (locked_theta >= Math.PI && locked_theta < (Math.PI + pi2))
{
points = new PointF[] {
new PointF( (float) adjacentTop, (float)newHeight ),
new PointF( 0.0F, (float) adjacentBottom ),
new PointF( (float)newWidth, (float)oppositeTop )
};
}
else
{
points = new PointF[] {
new PointF( 0.0F, (float) adjacentBottom ),
new PointF( (float) oppositeBottom, 0.0F ),
new PointF( (float) adjacentTop, (float)newHeight )
};
}
g.DrawImage(image, points);
}
return rotatedBmp;
}





I noticed in your example and in the other examples from the comments that the top and left sides of the rotated imaged don't get antialiased like the right and bottom edges do. Anyone found a solution for that??
Isaac Nicolay





I figured out a solution by talking to the author of the article, in your rotation function before you start the rotation, if you put a 1 pixel edge on the top and left sides of the image like:
Bitmap bit = new Bitmap(image.Width + 1, image.Height + 1);
Graphics gr = Graphics.FromImage(bit);
gr.Clear(Color.White);
gr.DrawImage(image, new Point(1, 1));
image = (System.Drawing.Image)bit;
it will antialias correclty. I chose white because I am showing it on a white background on a webpage. I would guess you should be able to use Color.Transparent if you are using it in a windows form or if you are doing a gif file.
Isaac Nicolay





I found this piece of Code very helpful too. Here is the VB.NET version of the main Function: RotateImage. Note that i inserted a line: g.Clear(Color.White) to solve the problem of extra black regions outside the image area (i think this will not be the problem for some situations).
Public Function RotateImage(ByVal image As Image, ByVal angle As Single) As Drawing.Bitmap If image Is Nothing Then Throw New ArgumentNullException("image") End If
Dim pi2 As Single = Math.PI / 2.0 Dim oldWidth As Single = image.Width Dim oldHeight As Single = image.Height
'Convert degrees to radians Dim theta As Single = angle * Math.PI / 180.0 Dim locked_theta As Single = theta
' Ensure theta is now [0, 2pi) If locked_theta < 0.0 Then locked_theta += 2 * Math.PI
Dim newWidth, newHeight As Single Dim nWidth, nHeight As Integer
Dim adjacentTop, oppositeTop As Single Dim adjacentBottom, oppositeBottom As Single
If (locked_theta >= 0.0 And locked_theta < pi2) Or _ (locked_theta >= Math.PI And locked_theta < (Math.PI + pi2)) Then 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 End If
newWidth = adjacentTop + oppositeBottom newHeight = adjacentBottom + oppositeTop
nWidth = Int(Math.Ceiling(newWidth)) nHeight = Int(Math.Ceiling(newHeight))
Dim rotatedBmp As New Drawing.Bitmap(nWidth, nHeight)
Dim g As Graphics = Graphics.FromImage(rotatedBmp)
g.Clear(Color.White)
'// This array will be used to pass in the three points that '// make up the rotated image Dim points(2) As Point
If (locked_theta >= 0.0 And locked_theta < pi2) Then
points(0) = New Point(Int(oppositeBottom), 0) points(1) = New Point(nWidth, Int(oppositeTop)) points(2) = New Point(0, Int(adjacentBottom))
ElseIf locked_theta >= pi2 And locked_theta < Math.PI Then
points(0) = New Point(nWidth, Int(oppositeTop)) points(1) = New Point(Int(adjacentTop), nHeight) points(2) = New Point(Int(oppositeBottom), 0)
ElseIf locked_theta >= Math.PI And locked_theta < (Math.PI + pi2) Then
points(0) = New Point(Int(adjacentTop), nHeight) points(1) = New Point(0, Int(adjacentBottom)) points(2) = New Point(nWidth, Int(oppositeTop))
Else
points(0) = New Point(0, Int(adjacentBottom)) points(1) = New Point(Int(oppositeBottom), 0) points(2) = New Point(Int(adjacentTop), nHeight) End If
g.DrawImage(image, points)
g.Dispose() image.Dispose()
Return rotatedBmp
End Function





Public Sub Rotate(ByRef imgSrc As Image, ByVal Angle As Single, ByVal BackColor As Color)
Dim dA As Double
Dim iPointsX(3) As Integer
Dim iPointsY(3) As Integer
Dim dR As Double = 0.5 * Math.Sqrt(Math.Pow(imgSrc.Height, 2) + Math.Pow(imgSrc.Width, 2))
Dim dAngleRad As Double = Math.PI * Angle / 180
Dim iK As Integer = 1
For I As Integer = 1 To 4
dA = Math.PI * (I \ 3) + iK * Math.Atan(imgSrc.Height / imgSrc.Width)
iPointsX(I  1) = dR * Math.Cos(dAngleRad + dA) + imgSrc.Width * 0.5
iPointsY(I  1) = dR * Math.Sin(dAngleRad + dA) + imgSrc.Height * 0.5
iK *= 1
Next
Dim pts() As Point = {New Point(iPointsX(3), iPointsY(3)), New Point(iPointsX(0), iPointsY(0)), New Point(iPointsX(2), iPointsY(2))}
Array.Sort(iPointsX)
Array.Sort(iPointsY)
Dim imgNew As Bitmap = New Bitmap(iPointsX(3)  iPointsX(0), iPointsY(3)  iPointsY(0))
Dim graSrc As Graphics = Graphics.FromImage(imgNew)
graSrc.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
graSrc.Clear(BackColor)
For I As Integer = 0 To 2
pts(I).X = iPointsX(0)
pts(I).Y = iPointsY(0)
Next
graSrc.DrawImage(imgSrc, pts)
graSrc.Dispose()
imgSrc = imgNew
End Sub





I tried to do the same thing myself but it is rotating and getting smaller oO
then i tried the two vb.net codes here and a get the same thing....
what can i do that the images is rotating and keeping its size...
i'm using Visual Studio 2010 Ultimate





Why not transform the points of the bounds of the image directly using the matrix? The resulting min/max values will form the new bounds... (ignore the generic function at the bottom, I just use it to get the min/max values out of a series of parameters).
private static Rectangle GetRotatedBounds(Bitmap bmp, Point rotationCenter, float angle) { // Create rotation matrix Matrix newMatrix = new Matrix(); newMatrix.RotateAt(angle, rotationCenter);
// Get bounds of the original unrotated image Rectangle bmpBounds = new Rectangle(new Point(0, 0), new Size(bmp.Width, bmp.Height));
// Construct rectangle with these bounds Point[] points = { new Point(bmpBounds.Left, bmpBounds.Top), new Point(bmpBounds.Right, bmpBounds.Top), new Point(bmpBounds.Right, bmpBounds.Bottom), new Point(bmpBounds.Left, bmpBounds.Bottom)};
// Rotate the points of the bounds newMatrix.TransformPoints(points);
// Get min/max values of the new bounds int maxX = BinaryReduce<int>(Math.Max, points[0].X, points[1].X, points[2].X, points[3].X); int minX = BinaryReduce<int>(Math.Min, points[0].X, points[1].X, points[2].X, points[3].X);
int maxY = BinaryReduce<int>(Math.Max, points[0].Y, points[1].Y, points[2].Y, points[3].Y); int minY = BinaryReduce<int>(Math.Min, points[0].Y, points[1].Y, points[2].Y, points[3].Y);
// Return resulting bounding rectangle return new Rectangle(new Point(minX, minY), new Size(maxX  minX, maxY  minY)); }
private delegate T BinaryFunction<T>(T a, T b);
private static T BinaryReduce<T>(BinaryFunction<T> function, params T[] values) { if (values.Length == 0) return default(T);
if(values.Length == 1) return values[0];
T result = values[0]; for(int i = 1; i < values.Length; i++) result = function(result, values[i]);
return result; }
This works well for me...
You can easily modify my code to get the boundaries of any rotated shape if you pass the pointsarray into the GetRotatedBounds as well instead of getting them from the image.





I really liked this approach to rotating an image using DrawImage. However I think you might have overcomplicated it a bit. You in fact only need to take the starting 3 points, rotate them about the centre, then draw the image between the rotated points
private static Bitmap AlternativeRotateImage( Image image, double angle )
{
/* From MSDN spec
* The destPoints parameter specifies three points of a parallelogram.
* The three PointF structures represent the upperleft, upperright,
* and lowerleft corners of the parallelogram
* */
Bitmap rotatedImage = new Bitmap( image.Width, image.Height );
PointF[] points = new PointF[3];
double radAngle = ( angle * Math.PI ) / 180;
double sin = Math.Sin( radAngle );
double cos = Math.Cos( radAngle );
double halfWidth = image.Width / 2;
double halfHeight = image.Height / 2;
double cosWidth = cos * halfWidth;
double cosHeight = cos * halfHeight;
double sinWidth = sin * halfWidth;
double sinHeight = sin * halfHeight;
points[ 0 ] = new PointF
(
(float)( halfWidth  cosWidth + sinHeight ),
(float)( halfHeight  cosHeight  sinWidth )
);
points[ 1 ] = new PointF
(
(float)( halfWidth + cosWidth + sinHeight ),
(float)( halfHeight  cosHeight + sinWidth )
);
points[ 2 ] = new PointF
(
(float)( halfWidth  cosWidth  sinHeight ),
(float)( halfHeight + cosHeight  sinWidth )
);
Graphics rotatedImageGraphics = Graphics.FromImage( rotatedImage );
rotatedImageGraphics.DrawImage( image, points );
return rotatedImage;
}





Did you test your alternative code?
Try to rotate an image by 30 degrees and see the image edges cropped off. Your code doesn't get the right image bounds.:>





Im rotating pics for a game and the use magenta (255,0,255) for transparent but as the image gets antialised it shows up with magenta borders (as theyre not perfectly magenta)
I tried with
g.SmoothingMode = SmoothingMode.None;
but the result is the same
 thomasa88





I like your article and it lead me in the direction I needed. I reworked your code so that my weak trig skills don't slow me down. I also decided to flip the Bitmap onto an new one and keep a copy of the old one for future rotations. You may want to take a look at your Graphics device. I don't think you call dispose() after you are done with it.
Have you found a way to AntiAlias the sides during the redraw?
public static Bitmap RotateImage2(Bitmap bmp, float angle)
{
Bitmap retbmp = null;
Point[] pts = new Point[]
{
new Point(0, 0),
new Point(bmp.Width, 0),
new Point(bmp.Width, bmp.Height),
new Point(0, bmp.Height)
};
for (int i = 0; i < 4; i++)
{
pts[i].X = bmp.Width / 2;
pts[i].Y = bmp.Height / 2;
}
Matrix m = new Matrix();
m.Rotate(angle);
m.TransformPoints(pts);
m.Dispose();
int maxX = int.MinValue;
int maxY = int.MinValue;
int minX = int.MaxValue;
int minY = int.MaxValue;
for (int i = 0; i < 4; i++)
{
if (maxX < pts[i].X) maxX = pts[i].X;
if (maxY < pts[i].Y) maxY = pts[i].Y;
if (minX > pts[i].X) minX = pts[i].X;
if (minY > pts[i].Y) minY = pts[i].Y;
}
for (int i = 0; i < 4; i++)
{
pts[i].X = minX;
pts[i].Y = minY;
}
retbmp = new Bitmap(maxX  minX, maxY  minY, PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(retbmp);
Point[] finalPts = new Point[] { pts[0], pts[1], pts[3] };
g.DrawImage(bmp, finalPts);
g.Dispose();
return retbmp;
}







General News Suggestion Question Bug Answer Joke Praise Rant Admin Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

