I am trying to rotate two bitmaps, then copy the results into a third one.
Bitmap bmp_1 = new Bitmap(Program.Properties.Resources.MyImage); // 100x100 px
Bitmap bmp_2 = new Bitmap(Program.Properties.Resources.MyImage); // 100x100 px
Bitmap bmp_merged = new Bitmap(200, 100, PixelFormat.Format32bppArgb);
float angle, bw2, bh2;
using (Graphics g = Graphics.FromImage(bmp_merged))
{
using (Graphics graphics = Graphics.FromImage(bmp_1))
{
angle = 15;
bw2 = bmp_1.Width / 2f;
bh2 = bmp_1.Height / 2f;
graphics.TranslateTransform(bw2, bh2);
graphics.RotateTransform(angle);
graphics.TranslateTransform(-bw2, -bh2);
graphics.DrawImage(bmp_1, 0, 0);
}
using (Graphics graphics = Graphics.FromImage(bmp_2))
{
angle = 35;
bw2 = bmp_2.Width / 2f;
bh2 = bmp_2.Height / 2f;
graphics.TranslateTransform(bw2, bh2);
graphics.RotateTransform(angle);
graphics.TranslateTransform(-bw2, -bh2);
graphics.DrawImage(bmp_2, 0, 0);
}
g.DrawImage(bmp_1, 0, 0);
g.DrawImage(bmp_2, 100, 0);
}
Issue:
After using graphics.DrawImage(bmp_1, 0, 0); I expected that bmp_1 will be a rotated image.
But it's actually the original bmp_1 image and its rotated version drawn over it.
Drawing into a Graphics object obtained from a Bitmap instance doesn't clear the Bitmap instance first. It just draws on top of whatever was there.
Instead of modifying the bitmaps you start with, you should just draw them rotated into the destination. For example, something like this:
using (Graphics g = Graphics.FromImage(bmp_merged))
{
angle = 15;
bw2 = bmp_1.Width / 2f;
bh2 = bmp_1.Height / 2f;
g.TranslateTransform(bw2, bh2);
g.RotateTransform(angle);
g.TranslateTransform(-bw2, -bh2);
g.DrawImage(bmp_1, 0, 0);
angle = 35;
bw2 = bmp_2.Width / 2f;
bh2 = bmp_2.Height / 2f;
g.ResetTransform();
g.TranslateTransform(bw2, bh2);
g.RotateTransform(angle);
g.TranslateTransform(-bw2, -bh2);
g.DrawImage(bmp_2, 0, 0);
}
Naturally, you should really factor the code for drawing a bitmap rotated at a specific angle out into its own method, so that you don't have two copies of the code. But the above should address your basic problem.
Related
How to I rotate an image without it showing like this?
Here's my Rotation Method:
public static Bitmap RotateImageN(Bitmap bmp, float angle)
{
Bitmap rotatedImage = new Bitmap(bmp.Width, bmp.Height);
using (Graphics g = Graphics.FromImage(rotatedImage))
{
// Set the rotation point to the center in the matrix
g.TranslateTransform(bmp.Width / 2, bmp.Height / 2);
// Rotate
g.RotateTransform(angle);
// Restore rotation point in the matrix
g.TranslateTransform(-bmp.Width / 2, -bmp.Height / 2);
// Draw the image on the bitmap
g.DrawImage(bmp, new Point(0, 0));
}
return rotatedImage;
}
Edit: After trying Loocid's Code
Your rotatedImage Bitmap needs to be big enough to accommodate the rotated image.
Say you rotated your original image by 30° you need to get the size of the bounding box like so:
Using some basic trig:
x = L*cos(30 * π / 180) + w*cos(60 * π / 180)
y = L*sin(30 * π / 180) + w*sin(60 * π / 180)
Therefore change the start of your code to:
var x = bmp.Width * Math.Cos(angle * Math.PI / 180) + bmp.Height * Math.Cos((90-angle) * Math.PI / 180)
var y = bmp.Width * Math.Sin(angle * Math.PI / 180) + bmp.Height * Math.Sin((90-angle) * Math.PI / 180)
Bitmap rotatedImage = new Bitmap(x, y);
The issue occurs in the rotating is related to the bounding box. It is clipping the edge because of the image you provided does not fit into the area that you have given.
I also faced this issue. So I tried a solution from here.
Adding the code that works for me.
public static Bitmap RotateImageN(Bitmap bitmap, float angle)
{
Matrix matrix = new Matrix();
matrix.Translate(bitmap.Width / -2, bitmap.Height / -2, MatrixOrder.Append);
matrix.RotateAt(angle, new System.Drawing.Point(0, 0), MatrixOrder.Append);
using (GraphicsPath graphicsPath = new GraphicsPath())
{
graphicsPath.AddPolygon(new System.Drawing.Point[] { new System.Drawing.Point(0, 0), new System.Drawing.Point(bitmap.Width, 0), new System.Drawing.Point(0, bitmap.Height) });
graphicsPath.Transform(matrix);
System.Drawing.PointF[] points = graphicsPath.PathPoints;
Rectangle rectangle = boundingBox(bitmap, matrix);
Bitmap resultBitmap = new Bitmap(rectangle.Width, rectangle.Height);
using (Graphics gDest = Graphics.FromImage(resultBitmap))
{
Matrix mDest = new Matrix();
mDest.Translate(resultBitmap.Width / 2, resultBitmap.Height / 2, MatrixOrder.Append);
gDest.Transform = mDest;
gDest.DrawImage(bitmap, points);
return resultBitmap;
}
}
}
private static Rectangle boundingBox(Image image, Matrix matrix)
{
GraphicsUnit graphicsUnit = new GraphicsUnit();
Rectangle boundingRectangle = Rectangle.Round(image.GetBounds(ref graphicsUnit));
Point topLeft = new Point(boundingRectangle.Left, boundingRectangle.Top);
Point topRight = new Point(boundingRectangle.Right, boundingRectangle.Top);
Point bottomRight = new Point(boundingRectangle.Right, boundingRectangle.Bottom);
Point bottomLeft = new Point(boundingRectangle.Left, boundingRectangle.Bottom);
Point[] points = new Point[] { topLeft, topRight, bottomRight, bottomLeft };
GraphicsPath graphicsPath = new GraphicsPath(points, new byte[] { (byte)PathPointType.Start, (byte)PathPointType.Line, (byte)PathPointType.Line, (byte)PathPointType.Line });
graphicsPath.Transform(matrix);
return Rectangle.Round(graphicsPath.GetBounds());
}
I have the following code which i wrote to try and rotate a bitmap(this is a test) the idea is to take a bitmap and rotate it by some amount of degrees and then draw it on the screen using win forms
protected override void OnDoubleClick(EventArgs e)
{
base.OnDoubleClick(e);
Graphics g = this.CreateGraphics();
Bitmap b = new Bitmap(path);
g.Clear(Color.White);
imagePosition = Cursor.Position;
b = RotateImage(b, 45);
g.DrawImage(b, new Point(100, 100));
}
public Bitmap RotateImage(Bitmap b, float angle)
{
Bitmap returnBitmap = new Bitmap(b.Width, b.Height);
returnBitmap.SetResolution(b.HorizontalResolution, b.VerticalResolution);
Graphics g = Graphics.FromImage(returnBitmap);
g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
g.RotateTransform(angle);
g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
g.DrawImage(b, new Point(0, 0));
return returnBitmap;
}
this is the image before rotation
and this is the image after rotating 45 degrees like was shown in the code
The reason it's getting clipped is that there isn't enough space to display it rotated. The diagonal is longer than the sides (Pythagoras).
You need to make more space for the image, then it should display OK. How you do that will depend on what the image is contained in.
I have created an Image and I want to add a rectangle around the image and recreate it as Image again to be drawn in a PictureBox
but I'm getting out of memory exception
How can I modify the image.
public void Draw(SizeF size)
{
int scale = 100;
Graphics refGraph = this.CreateGraphics();
IntPtr hdc = refGraph.GetHdc();
SolidBrush brush = new SolidBrush(Color.Black);
Pen pen = new Pen(Color.Black, 4);
try
{
Metafile image = new Metafile(hdc, EmfType.EmfOnly, "Shapes");
using (Graphics g = Graphics.FromImage(image))
{
PointF center = new PointF((float)base.Width / 2, base.Height / 2);
//Draw a rect
RectangleF Block = new RectangleF(new PointF(center.X - size.Width * scale / 2, center.Y - size.Height * scale / 2), new SizeF(size.Width * scale, size.Height * scale));
g.FillRectangle(brush, Block);
}
//Image = image;
ModifyImage(image);
}
finally
{
refGraph.ReleaseHdc(hdc);
refGraph.Dispose();
pen.Dispose();
brush.Dispose();
}
Invalidate();
}
public void ModifyImage(Metafile image)
{
Graphics g = Graphics.FromImage(image);
PointF center = new PointF((float)Image.Width / 2, Image.Height / 2);
int bufferAmount = 5;
g.DrawRectangle(Pens.White, center.X - (Image.Width + bufferAmount) / 2, center.Y - (Image.Height + bufferAmount) / 2, Image.Width + bufferAmount, Image.Height + bufferAmount);
pictureBox.Image = image;
}
Thanks
You can create a Graphics from the image using the FromImage method, then use Graphics drawing methods to draw whatever you like. Here is a code sample:
System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(yourImage);
//you may use any pen
graphics.DrawRectangle(System.Drawing.Pens.Blue,0,0,yourImageWidth,yourImageHeight)
yourPictureBox.Image = yourImage;
Create a Graphics object from your image and draw onto it by using a Pen defining the border strength and its color:
using (var gfx = Graphics.FromImage(img))
{
using (var pen = new Pen(MYCOLOR, 3)
gfx.DrawRectangle(pen, MYRECT)
}
Note, this will manipulate your source image directly. If you want to have two images, one with and another without a border, you should clone your image before you draw over it:
var imgWithBorder = img.Clone();
// work with imgWithBorder ...
How do I rotate image then move to the top left 0,0 without cutting off the image.
Please read the comments inside the code. I got stuck at STEP 3
I think using trigonometry should be able to solve this problem.
thanks
private Bitmap RotateImage(Bitmap b, float angle)
{
//create a new empty bitmap to hold rotated image
Bitmap returnBitmap = new Bitmap(b.Width, b.Height);
//make a graphics object from the empty bitmap
Graphics g = Graphics.FromImage(returnBitmap);
//STEP 1 move rotation point to top left
g.TranslateTransform((float)0, (float)0);
//STEP 2 rotate
g.RotateTransform(angle);
//STEP 3 move image back to top left without cutting off the image
//SOME trigonometry calculation here
int newY = b.Height;
g.TranslateTransform(-(float)0, -newY);
//draw passed in image onto graphics object
g.DrawImage(b, new Point(0, 0));
return returnBitmap;
}
Does this cover the 'trigonometry'? I have made it step 0 because I think you need to do it first. That way you can calculate the size of the resulting bitmap, which will be bigger - see my comments in the code.
private Bitmap RotateImage(Bitmap b, float Angle) {
// The original bitmap needs to be drawn onto a new bitmap which will probably be bigger
// because the corners of the original will move outside the original rectangle.
// An easy way (OK slightly 'brute force') is to calculate the new bounding box is to calculate the positions of the
// corners after rotation and get the difference between the maximum and minimum x and y coordinates.
float wOver2 = b.Width / 2.0f;
float hOver2 = b.Height / 2.0f;
float radians = -(float)(Angle / 180.0 * Math.PI);
// Get the coordinates of the corners, taking the origin to be the centre of the bitmap.
PointF[] corners = new PointF[]{
new PointF(-wOver2, -hOver2),
new PointF(+wOver2, -hOver2),
new PointF(+wOver2, +hOver2),
new PointF(-wOver2, +hOver2)
};
for (int i = 0; i < 4; i++) {
PointF p = corners[i];
PointF newP = new PointF((float)(p.X * Math.Cos(radians) - p.Y * Math.Sin(radians)), (float)(p.X * Math.Sin(radians) + p.Y * Math.Cos(radians)));
corners[i] = newP;
}
// Find the min and max x and y coordinates.
float minX = corners[0].X;
float maxX = minX;
float minY = corners[0].Y;
float maxY = minY;
for (int i = 1; i < 4; i++) {
PointF p = corners[i];
minX = Math.Min(minX, p.X);
maxX = Math.Max(maxX, p.X);
minY = Math.Min(minY, p.Y);
maxY = Math.Max(maxY, p.Y);
}
// Get the size of the new bitmap.
SizeF newSize = new SizeF(maxX - minX, maxY - minY);
// ...and create it.
Bitmap returnBitmap = new Bitmap((int)Math.Ceiling(newSize.Width), (int)Math.Ceiling(newSize.Height));
// Now draw the old bitmap on it.
using (Graphics g = Graphics.FromImage(returnBitmap)) {
g.TranslateTransform(newSize.Width / 2.0f, newSize.Height / 2.0f);
g.RotateTransform(Angle);
g.TranslateTransform(-b.Width / 2.0f, -b.Height / 2.0f);
g.DrawImage(b, 0, 0);
}
return returnBitmap;
}
There are some looooong and hungry algorithms for doing so, but as of yet I haven't come up with or found anything particularly fast.
The fastest way is to this is to use unsafe calls to manipulate the image memory directly using LockBits. It sounds scary but it's pretty straight forward. If you search for LockBits you'll find plently of examples such as here.
The interesting bit is:
BitmapData originalData = originalBitmap.LockBits(
new Rectangle(0, 0, originalWidth, originalHeight),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppRgb);
Once you have the BitmapData you can pass the pixels and map them into a new image (again using LockBits). This is significantly quicker than using the Graphics API.
Here's what I ended up doing (after an extensive amount of continued research, and the helpful link provided by TheCodeKing):
public Image RotateImage(Image img, float rotationAngle)
{
// When drawing the returned image to a form, modify your points by
// (-(img.Width / 2) - 1, -(img.Height / 2) - 1) to draw for actual co-ordinates.
//create an empty Bitmap image
Bitmap bmp = new Bitmap((img.Width * 2), (img.Height *2));
//turn the Bitmap into a Graphics object
Graphics gfx = Graphics.FromImage(bmp);
//set the point system origin to the center of our image
gfx.TranslateTransform((float)bmp.Width / 2, (float)bmp.Height / 2);
//now rotate the image
gfx.RotateTransform(rotationAngle);
//move the point system origin back to 0,0
gfx.TranslateTransform(-(float)bmp.Width / 2, -(float)bmp.Height / 2);
//set the InterpolationMode to HighQualityBicubic so to ensure a high
//quality image once it is transformed to the specified size
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
//draw our new image onto the graphics object with its center on the center of rotation
gfx.DrawImage(img, new PointF((img.Width / 2), (img.Height / 2)));
//dispose of our Graphics object
gfx.Dispose();
//return the image
return bmp;
}
Cheers!
void Graphics.RotateTransform(float angle);
This should rotate the image in C#. What is it doing instead?
I haven't experimented too much with GDI+. Remember to reverse the rotation after the image is drawn.
This answer returns both the offset it should be drawn on and the image which has been rotated.It works by recreating the new image to the size it should be without clipping the angles. Originally written by Hisenburg from the #C# IRC chatroom and Bloodyaugust.
public static double NormalizeAngle(double angle)
{
double division = angle / (Math.PI / 2);
double fraction = Math.Ceiling(division) - division;
return (fraction * Math.PI / 2);
}
public static Tuple<Image,Size> RotateImage(Image img, double rotationAngle)
{
double normalizedRotationAngle = NormalizeAngle(rotationAngle);
double widthD = img.Width, heightD = img.Height;
double newWidthD, newHeightD;
newWidthD = Math.Cos(normalizedRotationAngle) * widthD + Math.Sin(normalizedRotationAngle) * heightD;
newHeightD = Math.Cos(normalizedRotationAngle) * heightD + Math.Sin(normalizedRotationAngle) * widthD;
int newWidth, newHeight;
newWidth = (int)Math.Ceiling(newWidthD);
newHeight = (int)Math.Ceiling(newHeightD);
Size offset = new Size((newWidth - img.Width) / 2,(newHeight - img.Height) / 2);
Bitmap bmp = new Bitmap(newWidth, newHeight);
Graphics gfx = Graphics.FromImage(bmp);
//gfx.Clear(Color.Blue);
gfx.TranslateTransform((float)bmp.Width / 2, (float)bmp.Height / 2);
gfx.RotateTransform((float)(rotationAngle / Math.PI * 180));
gfx.TranslateTransform(-(float)bmp.Width / 2, -(float)bmp.Height / 2);
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfx.DrawImage(img, new PointF((bmp.Width / 2 - img.Width / 2), (bmp.Height / 2 - img.Height / 2)));
gfx.Dispose();
return new Tuple<Image,Size>(bmp,offset);
}
System.Drawing.Image imageToRotate = System.Drawing.Image.FromFile(imagePath);
switch (rotationAngle.Value)
{
case "90":
imageToRotate.RotateFlip(RotateFlipType.Rotate90FlipNone);
break;
case "180":
imageToRotate.RotateFlip(RotateFlipType.Rotate180FlipNone);
break;
case "270":
imageToRotate.RotateFlip(RotateFlipType.Rotate270FlipNone);
break;
default:
throw new Exception("Rotation angle not supported.");
}
imageToRotate.Save(imagePath, ImageFormat.Jpeg);