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.
Related
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 ...
So I have a picturebox with a background image of size 123x123 pixels.
Everytime the image is clicked I make it rotate by a certain angle using the function below.
//The original image to rotate.
private readonly Bitmap _origPowerKnob = Properties.Resources.PowerKnob;
//Calling the rotation function.
using (Bitmap b = new Bitmap(_origPowerKnob))
{
Bitmap newBmp = RotateImage(b, _powerAngle);
PowerKnob.BackgroundImage = newBmp;
}
//Do the rotation.
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);
//move rotation point to center of image.
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.TranslateTransform(b.Width / 2F, b.Height / 2F);
//Rotate.
g.RotateTransform(angle);
//Move image back.
g.TranslateTransform(-b.Width / 2F, -b.Height / 2F);
//Draw passed in image onto graphics object.
g.DrawImage(b, new PointF(0,0));
return returnBitmap;
}
The problem is the image in not correctly rotating around the center. It is a little off and I can't seem to understand why. Any suggestions?
I have a small error when trying to rotate an image within a picture box.
It all works. But when rotating, it doesn't rotate perfectly around the center. It's slightly off (not very noticeable) but kinda annoying. Here is my code:
private readonly Bitmap _origPowerKnob = Properties.Resources.PowerKnob;
//CODE WHERE ROTATE METHOD IS CALLED//
using (Bitmap b = new Bitmap(_origPowerKnob))
{
Bitmap newBmp = RotateImage(b, _powerAngle);
PowerKnob.BackgroundImage = newBmp;
}
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);
//move rotation point to center of image.
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
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;
}
Pictures showing what I mean:
It doesn't rotate perfectly. Is there a solution to this? Something I haven't set for my picturebox properties? I've tried alot.
Thanks.
I found a solution to get the rotated image from it's center
[My Testing Conditions]
I'm testing this with the next considerations, if it works for you its ok :3
1.-The source image i used is a perfect square[LxL], taked from a png file, size of the square no matter, just needs to be LxL; //(Width == Height) = true;
2.- The png source image file that i am rotating is transparent and square shaped, i got it from illustrator
3.- I tested files of size 417x417, and 520x520, and 1024x1024
[The code i can share to you]
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace YourNamespace
{
public static class TheClassinWhichYouWantToPlaceTheFunction
{
public static Bitmap RotateImageN(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);
//move rotation point to center of image.
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
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.
//Found ERROR 1: Many people do g.DwarImage(b,0,0); The problem is that you re giving just the position
//Found ERROR 2: Many people do g.DrawImage(b, new Point(0,0)); The size still not present hehe :3
g.DrawImage(b, 0,0,b.Width, b.Height); //My Final Solution :3
return returnBitmap;
}
}
}
I just gived the name "RotateImageN" to the function, because is the 5th solution I have tried :3 i Hope to be helpful
Sorry for my english and/or grammar hehe :3
This is where a simple test form would have helped you a lot.
Take this code and put it in a new WinForms project's Form1.cs file.
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace RotateImage {
public partial class Form1 : Form {
private readonly Graphics gfx;
private readonly Bitmap originalBitmap;
private readonly Bitmap redrawnBitmap;
private readonly Stopwatch sw;
private Timer timer;
public Form1() {
InitializeComponent();
BackColor = Color.White;
timer = new Timer();
timer.Interval = 16;
timer.Enabled = true;
timer.Tick += timer_Tick;
sw = new Stopwatch();
sw.Start();
gfx = CreateGraphics();
originalBitmap = new Bitmap(256, 256);
redrawnBitmap = new Bitmap(256, 256);
using (var bmpGfx = Graphics.FromImage(originalBitmap)) {
DrawCross(bmpGfx, new Point(128, 128), 128D, 0D);
}
}
private void timer_Tick(object sender, EventArgs e) {
// Rotate a full 90 degrees every 4 seconds.
var angle = sw.Elapsed.TotalSeconds * 22.5D;
var newBitmap = RotateImage(originalBitmap, (float)angle);
// Clear the result of the last draw.
gfx.FillRectangle(Brushes.White, new Rectangle(0, 0, 256, 256));
gfx.DrawImageUnscaled(newBitmap, 0, 0);
gfx.DrawEllipse(Pens.Blue, new Rectangle(124, 124, 8, 8));
using (var redrawGfx = Graphics.FromImage(redrawnBitmap)) {
// Clear what we have, we are redrawing on the same surface.
redrawGfx.Clear(Color.White);
DrawCross(redrawGfx, new Point(128, 128), 128D, angle);
}
gfx.DrawImageUnscaled(redrawnBitmap, 256, 0);
gfx.DrawEllipse(Pens.Blue, new Rectangle(256+124, 124, 8, 8));
}
private void DrawCross(Graphics drawGfx, Point center, double radius, double angle) {
// Turn our angle from degrees to radians.
angle *= Math.PI / 180;
// NOTE: Using PointF to lazily "fix" rounding errors and casting (flooring) double to int. When the result of the math below is say 127.9999999...
// then it would get rounded down to 127. There is always Math.Round, which can round to nearest whole (away from .5) integer!
// Draw one line of our cross.
drawGfx.DrawLine(
Pens.Red,
new PointF((float)(Math.Cos(angle) * radius + center.X), (float)(Math.Sin(angle) * radius + center.Y)),
new PointF((float)(Math.Cos(angle - Math.PI) * radius + center.X), (float)(Math.Sin(angle - Math.PI) * radius + center.Y)));
// Rotate our angle 90 degrees.
angle += Math.PI / 2D;
// Draw the other line of our cross.
drawGfx.DrawLine(
Pens.Red,
new PointF((float)(Math.Cos(angle) * radius + center.X), (float)(Math.Sin(angle) * radius + center.Y)),
new PointF((float)(Math.Cos(angle - Math.PI) * radius + center.X), (float)(Math.Sin(angle - Math.PI) * radius + center.Y)));
}
// Your method, not mine.
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);
//move rotation point to center of image.
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
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;
}
}
}
Observe as the two crosses rotate about their center just fine. The left one being a bitmap that is rotated with your method, the right one being redrawn every frame.
That is, there is nothing wrong with your rotation code but it's possible there's something wrong with your source bitmap or your display container.
When you use transparent png with just a portion of the image filled with color for example - the code will rotate transform only the portion of the png that has data in it...
I tried to get a dot in the top center of a png to rotate around the center of the image (500x500 px) and it just recognizes the part that was colored, so it turned into a bounce effect.
I tried with a fully colored 500x500 px image too, and that worked normally..
Hope it helps a little!
So basically what I want to do is overlap two .PNG images with transparent backgrounds. One is with a shotgun which rotates to mouse position, and the other is a cartoon character which I want to put behind the shotgun. Now, the problem is everytime I overlap them, the transparent background of the PNG image gets in the way and I can't see the shooter at all.
I have tried putting the shooter in a panel but placing the shotgun picturebox within it screws up the rotation algorithm (makes it rotate very slowly), I have no idea why.
Any help would be apreciated, thanks.
Coding I used:
Rotation algorithm:
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);
//move rotation point to center of image
g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
//rotate
g.RotateTransform((int)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;
}
private float CalcAngle(Point TargetPos)
{
Point ZeroPoint = new Point(pictureBox1.Location.X + pictureBox1.Width / 2, pictureBox1.Location.Y + pictureBox1.Height / 2);
if (TargetPos == ZeroPoint)
{
return 0;
}
double angle;
double deltaX, deltaY;
deltaY = TargetPos.Y - ZeroPoint.Y;
deltaX = TargetPos.X - ZeroPoint.X;
angle = Math.Atan2(deltaY, deltaX) * 180 / Math.PI;
return (float)angle;
}
private void timer1_Tick(object sender, EventArgs e)
{
pictureBox1.Image = (Bitmap)backup.Clone();
//Load an image in from a file
Image image = new Bitmap(pictureBox1.Image);
//Set our picture box to that image
pictureBox1.Image = (Bitmap)backup.Clone();
//Store our old image so we can delete it
Image oldImage = pictureBox1.Image;
//Set angle
angle = CalcAngle(new Point(Cursor.Position.X, Cursor.Position.Y - 10));
//Pass in our original image and return a new image rotated X degrees right
pictureBox1.Image = rotateImage((Bitmap)image, angle);
if (oldImage != null)
{
oldImage.Dispose();
image.Dispose();
}
}
When you are creating a new Bitmap try to use any of the pixel format for 32 bpp or 64 bpp. See the code below:
Bitmap returnBitmap = new Bitmap(b.Width, b.Height, PixelFormat.Format64bppPArgb);
Here I draw three different png files on top of each other onto a panel:
using (Graphics graphic = panel1.CreateGraphics())
{
using (Image image = Image.FromFile(#"D:\tp3.png")) graphic.DrawImage(image, Point.Empty);
using (Image image = Image.FromFile(#"D:\tp2.png")) graphic.DrawImage(image, Point.Empty);
using (Image image = Image.FromFile(#"D:\tp1.png")) graphic.DrawImage(image, Point.Empty);
}
If you create a new BitMap do as #Palak.Maheria said and use a 32bit format with an alpha channel!
I am using OnPaint method in my class Class1 : Panel.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
}
to rotate and draw rectangle I am using
Matrix m = new Matrix();
m.RotateAt(90, rotationPoint);
g.Transform = m;
g.FillRectangle(Brushes.Black, rectangle)
the problem is, that rotation isn't working as I want it to.
Red square is rotation point and it's located in the middle-top of rectangle. How to set x, y and rotation point so rotation would work properly?
After rotating at 90 degress it should look like this
red pixel is still at the same location.
Rotation point is not the point, which you want to rotate. It is point, around which graphics is rotated. So if you draw a rectangle on the top of the graphics and want to rotate it (rectangle) - then you should set rotation point as center of graphics and rotate image to 90 degrees.
Here is example, that does almost what you want:
base.OnPaint(e);
var g = e.Graphics;
var width = g.VisibleClipBounds.Width;
var height = g.VisibleClipBounds.Height;
var rotationPoint = new PointF(width / 2, height / 2); ;
// draw center point
g.FillRectangle(Brushes.Red, new RectangleF(rotationPoint.X - 5, rotationPoint.Y - 5, 10, 10));
using (var path = new GraphicsPath())
{
var rectangle = new RectangleF((width - 10) / 2, 0, 10, 10);
var m = new Matrix();
m.RotateAt(90, rotationPoint);
path.AddRectangle(rectangle);
path.Transform(m);
// draw rotated point
g.FillPath(Brushes.Black, path);
}