I need to rotate an image by a certain angle before further operations. After rotating, im left with black areas on my bitmap.
Image after rotating by 30 degrees
I've managed to find similar issue in java right here.
Is there something like that for c#?
Im using the following code, that i found earlier on stack:
Bitmap Rotate_Image(Bitmap bmp, float angle)
{
Bitmap rotatedImage = new Bitmap(bmp.Width, bmp.Height);
rotatedImage.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
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;
}
Since your output is *.png I assume you want to use a transparent background. Just call g.Clear(Color.FromArgb(0, default)); before applying TranslateTransform. Or just set any other background color if you like.
You also can set g.SmoothingMode = SmoothingMode.AntiAlias; to smoothen the sides of the rotated image.
Related
I'm having some issues with drawing ellipses to a bitmap in a .NET 4.0 C# WinForms application running on mono 4.6.2 on Raspbian 9 (stretch).
The method in question generates a bitmap of 5 white circles of a specified diameter on a black screen. One circle per corner of the screen, and one in the center. The bitmap is meant to encompass the entire screen, so I'm getting the size for the bitmap as a rectangle via Screen.Bounds(), which in turn gives me Res.Width and Res.Height.
This code works as expected in Windows in .NET (which is what it was originally written for and tested on) but running the exe through mono on Raspbian, simply fills the bitmap with white. I have also found that running in mono for Windows (v5.10.0.160), the circles generated end up being smaller than when run in .NET.
private Bitmap GenerateCirclesBitmap(int diam)
{
Bitmap bmp = GenerateColorBitmap(Color.Black);
Graphics g = Graphics.FromImage(bmp);
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
Brush whiteBrush = new SolidBrush(Color.White);
g.FillEllipse(whiteBrush, diam/7, diam/7, diam, diam); //Top Left Corner
g.FillEllipse(whiteBrush, diam/7, (Res.Height - diam)-(diam/7), diam, diam); //Bottom Left Corner
g.FillEllipse(whiteBrush, (Res.Width - diam) - (diam / 7), (Res.Height - diam) - (diam / 7), diam, diam); //Bottom Right Corner
g.FillEllipse(whiteBrush, (Res.Width - diam) - (diam / 7), diam/7, diam, diam); //Top Right Corner
g.FillEllipse(whiteBrush, (Res.Width / 2) - (diam / 2), (Res.Height / 2) - (diam / 2), diam, diam); //Center
g.DrawImage(bmp, Res.Width, Res.Height);
return bmp;
}
I was thinking maybe it was an issue with the dimensions of the screen, Res.Width and/or Res.Height, but if that were the case I would expect my method to generate color bars would have problems as well. However this code works as expected (8 color bars each 1/8 of total screen width) on both versions of mono as well as in .NET, which leads me to think that Res.Width is OK:
private Bitmap GenerateBarsBitmap(Color bar1, Color bar2, Color bar3, Color bar4, Color bar5, Color bar6)
{
Bitmap bmp = GenerateColorBitmap(Color.Black);
int barwidth = Res.Width / 8;
Color[] colors = new Color[8];
Brush b = new SolidBrush(Color.White);
Graphics g = Graphics.FromImage(bmp);
colors[0] = Color.White;
colors[1] = bar1;
colors[2] = bar2;
colors[3] = bar3;
colors[4] = bar4;
colors[5] = bar5;
colors[6] = bar6;
colors[7] = Color.Black;
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
for (int colornum = 0; colornum < colors.Length; colornum++)
{
b = new SolidBrush(colors[colornum]);
g.FillRectangle(b, barwidth * colornum, 0, barwidth, Res.Height);
b.Dispose();
}
return bmp;
}
Any ideas or insight would be appreciated. Thanks!
One difference between your two routines is the line
g.DrawImage(bmp, Res.Width, Res.Height);
It comes after you draw the circles. So in a fully compatible world the Mono framework should do the same as .NET does, i.e. draw the bitmap with all the new content over itself.
But fully compatible is not just hard. Given all the weirdness in the long history of GDI, .NET etc, it is in fact out of the question..
Mono seems to use the original version of the bitmap without the newly drawn circles and by drawing it over itself it deletes them.
So remove the unnessesary line and all should be well..
I have following code to draw my border2.bmp in 4 direction
private void Form1_Paint(object sender, PaintEventArgs e)
{
Bitmap border = new Bitmap("border2.bmp");
int borderThick = border.Height;
Graphics g = e.Graphics;
Size region = g.VisibleClipBounds.Size.ToSize();
Rectangle desRectW = new Rectangle(0, 0, region.Width - borderThick, borderThick);
// 1. LEFT - RIGHT border
g.TranslateTransform(30, 30);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
// 2. UP - BUTTOM border
g.ResetTransform();
g.TranslateTransform(50, 50);
g.RotateTransform(90);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
// 3. RIGHT-LEFT border
g.ResetTransform();
g.TranslateTransform(100, 100);
g.RotateTransform(180);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
// 4. BOTTOM - UP border
g.ResetTransform();
g.TranslateTransform(150, 150);
g.RotateTransform(270);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
}
My original image is:
But the result of rotations are not exactly as I expected. 90 degrees is missing the first red line, 270 degrees is missing first black column, and 180 degrees is missing both.
Like image I attached:
PS: you can get border2.bmp at: http://i.imgur.com/pzonx3i.png
Edit:
I tried g.PixelOffsetMode = PixelOffsetMode.HighQuality; as #Peter Duniho comment, but I found it also does't draw correctly.
Example: 4 line is not starting at same position as we expect.
g.TranslateTransform(50, 50);
// LEFT - RIGHT border
g.DrawLine(Pens.Red, 0, 0, 100, 0);
// UP - BOTTOM border
g.RotateTransform(90);
g.DrawLine(new Pen(Color.FromArgb(128, Color.Blue)), 0, 0, 100, 0);
// RIGHT-LEFT border
g.RotateTransform(90);
g.DrawLine(new Pen(Color.FromArgb(128, Color.Green)), 0, 0, 100, 0);
// BOTTOM - UP border
g.RotateTransform(90);
g.DrawLine(new Pen(Color.FromArgb(128, Color.Gray)), 0, 0, 100, 0);
I can't really explain why this happens, except that any graphics API is necessarily going to include optimizations which may lead to imprecise behaviors at times and it seems that you are running into such a situation here.
In your particular example, the problem can be corrected by adding the following statement to the code, before you draw the images:
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
Setting it to Half will also work. It is equivalent to HighQuality (or technically, HighQuality is equivalent to it…but I find HighQuality more descriptive in the code :) ).
This will slow the rendering of the bitmap down somewhat, but probably not in a way that is perceptible to your users.
While the .NET documentation isn't very helpful in terms of describing this setting, the native Win32 docs for the same feature has slightly more detail:
PixelOffsetMode enumeration. From the description, one can infer that with the logical center of the pixel at (0,0), it's possible to lose a pixel on one edge when rotating (and/or gain a pixel on another edge). Switching to Half fixes this.
You need to account some things.
(1) RotateTransforms applies rotation at the current origin (as explained in the Matrix.Rotate documentation
(2) The default MatrixOrder (when not specified by using the overrides with the additional argument) is Prepend. Which means in your case the resulting transformation is rotate, then translate.
For instance, if you put this code inside the paint event:
var g = e.Graphics;
var loc = new Point(128, 128);
var rect = new Rectangle(0, 0, 64, 16);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.FillRectangle(Brushes.Blue, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.RotateTransform(90);
g.FillRectangle(Brushes.Red, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.RotateTransform(180);
g.FillRectangle(Brushes.Green, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.RotateTransform(270);
g.FillRectangle(Brushes.Magenta, rect);
you'll get this
The blue rectangle is not rotated. Now pin virtually the origin (the upper left point) and start rotating clockwise. You'll see that it will exactly match the Red (90), Green (180) and Magenta (270) rectangles.
What all that means is that if you want to form a rectangle, you need to apply additional offset (translation) to the rotated rectangles. It depends how you want to handle the overlapping areas, but for the sample if we want to concat the Red rectangle right to the Blue one, we need to add the Blue rectangle Width + the original rectangle Height in the X direction. For other rotated rectangles you can apply similar additional offset to X, Y or both.
To complete the sample, if we modify the code like this
var g = e.Graphics;
var loc = new Point(128, 128);
var rect = new Rectangle(0, 0, 64, 16);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.FillRectangle(Brushes.Blue, rect);
g.ResetTransform();
g.TranslateTransform(loc.X + rect.Width + rect.Height, loc.Y);
g.RotateTransform(90);
g.FillRectangle(Brushes.Red, rect);
g.ResetTransform();
g.TranslateTransform(loc.X + rect.Width + rect.Height, loc.Y + rect.Width + rect.Height);
g.RotateTransform(180);
g.FillRectangle(Brushes.Green, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y + rect.Width + rect.Height);
g.RotateTransform(270);
g.FillRectangle(Brushes.Magenta, rect);
the new result will be
Once you understand all that, hope you can apply the required corrections to your concrete code.
I am trying to draw 2 rectangles 1 in green channel 2nd in red channel of a bitmap.
This is the code:
Bitmap bitmap_guess = new Bitmap(C_PALETTE_X_PIXEL_MAX, C_PALETTE_Y_PIXEL_MAX, PixelFormat.Format24bppRgb);
Graphics graphics = Graphics.FromImage(bitmap_guess);
Rectangle box_rect = new Rectangle(0, 0, C_BOX_PIXELS_WIDTH, C_BOX_PIXELS_HEIGHT);
matrix = new Matrix();
matrix.RotateAt(thc, new System.Drawing.PointF(xc, yc), MatrixOrder.Prepend);
graphics.Transform = matrix;
graphics.FillRectangle(new SolidBrush(Color.Green), xc, yc, box_rect.Width, box_rect.Height);
matrix = new Matrix();
matrix.RotateAt(th, new System.Drawing.PointF(x, y), MatrixOrder.Prepend);
graphics.Transform = matrix;
graphics.FillRectangle(new SolidBrush(Color.Red), x, y, box_rect.Width, box_rect.Height);
The problem is that when I draw the 2nd rectangle in red color it overwrites the 1st rectangles overlapping pixels to 0's. I'd want the rectangles to to change color when they overlap instead of plainly overwriting the previous pixel values. IE - to draw in a single channel instead of all 3.
I'm specifically looking for a Graphics operation for this.
Kind regards,
Laov
This might help you Merging two images in C#/.NET
You then could draw each rectangle into its own image and merge the results...
On the left is the polygon I drew using my paint-like program. On the right is the polygon drawn by System.Drawing.Bitmap/Graphics:
The code to draw it is as follows:
protected static Bitmap CropImage(Bitmap src, Vector2[] rect)
{
var result = new Bitmap(src.Width, src.Height);
using (Graphics g = Graphics.FromImage(result))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
var pen = new Pen(Color.White);
g.DrawImage(src, new Point(0,0));
var poly = rect.Select(p => p.ToPointF()).ToArray();
g.DrawPolygon(pen, poly);
}
return result;
}
poly is:
{System.Drawing.PointF[4]}
[0]: {X = 57.4230042 Y = 57.4229736}
[1]: {X = 147.058868 Y = 56.0224}
[2]: {X = 148.43277 Y = 143.951767}
[3]: {X = 58.7969131 Y = 145.352341}
Each of the black squares in the image is 50x50. If you look at poly, all the coordinates are exactly as you'd expect: poly[0] is a little above 50,50 which corresponds to inside the black square labelled "6" (as shown in left image).
So how is Graphics getting confused and putting it in the wrong spot? It looks like it's scaling the whole rect down.
You are confused. The rectangle is correct, but the background image is scaled up. Note that the 6 and the black square are a lot bigger, but the rectangle is the exact same size.
Note that the Graphics.DrawImage method scales the source image to match the destination resolution.
This method draws an image using its physical size, so the image will have its correct size in inches regardless of the resolution (dots per inch) of the display device. For example, suppose an image has a pixel width of 216 and a horizontal resolution of 72 dots per inch. If you call this method to draw that image on a device that has a resolution of 96 dots per inch, the pixel width of the rendered image will be (216/72)*96 = 288.
You should use the overload that accepts a Rectangle instead:
g.DrawImage(src, new Rectangle(0, 0, src.Width, src.Height));
What is the best way to rotate a image in asp.net
I did use matrix.rotateAt but i can't get it to work so please tell me what is the best way?
I should write out that hate to rotate a image with the image object.
Image myImage = Image.FromFile("myimage.png");
myImage.RotateFlip(RotateFlipType.Rotate180FlipNone);
http://msdn.microsoft.com/en-us/library/system.drawing.image.rotateflip.aspx
Here is some sample code (not written by me - found some time ago here ) that worked for me, as long as you edit some details.
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
using (Graphics g = Graphics.FromImage(returnBitmap))
{
//move rotation point to center of image
g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
//rotate
g.RotateTransform(angle);
//move image back
g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
//draw passed in image onto graphics object
g.DrawImage(b, new Point(0, 0));
}
return returnBitmap;
}
Please note, that this may not work "out of the box" - there are some issues with the new bitmap. When you rotate it, it may not fit comfortably in the rectangle of the old bitmap (rectangle bounds b.Width, B.Height).
Anyway this is just to give you an idea. If you choose to do it this way, I'm sure you will be able to work out all the details. I'd post my final code, however I don't have it on me right now...
I would suggest this is the best way
// get the full path of image url
string path = Server.MapPath(Image1.ImageUrl) ;
// creating image from the image url
System.Drawing.Image i = System.Drawing.Image.FromFile(path);
// rotate Image 90' Degree
i.RotateFlip(RotateFlipType.Rotate90FlipXY);
// save it to its actual path
i.Save(path);
// release Image File
i.Dispose();
// Set Image Control Attribute property to new image(but its old path)
Image1.Attributes.Add("ImageUrl", path);
for more