erasing parts of the loaded image by mouse - c#

I have loaded an image in my panel.I want to erase the parts of that image by using the mouse(dragging on the panel).Here is my code to load my image:
private void drawP_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(myImage, new Point(0, 0));
}
How can I do it?
Thanks in advance.
Updated:
sorry not to say earlier,I have set another image(image2) as background of the panel and I want it to be seen after erasing myImage(image loaded with the code above).

Hi I'm going to assume that you want this feature to work like the eraser on paint.
there are 3 events you are going to need
1.mousedown - to call the first erase and open up the mousemove event method.
2.mouseup - to stop the mousemove event method
3.mousemove - just to call the erase method
Code: //part pseudo as im not in visual studio right now :(
//global vars
bool enable = false;
void erase(Point mousepoint)
{
Point f = (mousepoint.X - yourpanel.left?, mousepoint.Y - yourpanel.top?);
//gets mouse position on accual picture;
yourImageGraphics.fillreactangle( f.X - 10, f.Y+10, 20,20 ,Color.White)
// int X , int Y, width , height, color
}
void mousedown(?)
{
enable=true;
erase(Cursor.Position //but you get this from e?);
}
void mouseup(?);
{
enable=false;
}
void mousemove(?)
{
if (enable)
erase(e.Position?);
}
Also it looks like you are going to have to make a graphics object for your panel :(
I hope this helps because question was a bit vague.

Here I created simple example. Of course it can be done better, but I just wondering how to do it... so sharing my results.
public partial class mainForm : Form
{
private Bitmap image;
private Rectangle imgRect;
public mainForm()
{
InitializeComponent();
BackColor = Color.Chartreuse;
image = new Bitmap(#"C:\image.jpg");
imgRect = new Rectangle(0,0,image.Width, image.Height);
}
private void main_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(image, 0, 0);
}
private void main_MouseMove(object sender, MouseEventArgs e)
{
if(e.Button == MouseButtons.Left && e.X < image.Width && e.Y < image.Height)
{
image.SetPixel(e.X, e.Y, Color.Magenta);//change pixel color;
image.MakeTransparent(Color.Magenta);//Make image transparent
Invalidate(imgRect);
}
}
}
...and lets test
Ha! scared that I deleted his eye :)

A TextureBrush on a pen can be used for erasing.
Working example (image1 and image2 are the same size images):
Bitmap bmp1;
TextureBrush tb;
Point _LastPoint;
public Form1()
{
InitializeComponent();
this.DoubleBuffered = true;
bmp1 = new Bitmap(#"c:\image1.png");
tb = new TextureBrush(new Bitmap(#"c:\image2.png"));
}
private void Form1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
if (!_LastPoint.IsEmpty) {
using (Graphics g = Graphics.FromImage(bmp1))
using (Pen p = new Pen(tb, 15)) {
p.StartCap = LineCap.Round;
p.EndCap = LineCap.Round;
g.DrawLine(p, _LastPoint, e.Location);
}
}
_LastPoint = e.Location;
this.Invalidate();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e) {
_LastPoint = Point.Empty;
}
private void Form1_Paint(object sender, PaintEventArgs e) {
e.Graphics.DrawImage(bmp1, new Point(0, 0));
}

Related

I made a rectangle how do I check if the mouse clicked on it?

How do I check if a mouse clicked a rectangle?
Graphics gfx;
Rectangle hitbox;
hitbox = new hitbox(50,50,10,10);
//TIMER AT THE BOTTOM
gfx.Draw(System.Drawing.Pens.Black,hitbox);
Just a sample quick and dirty, if your "gfx" is a "e.Graphics..." from a Form:
public partial class Form1 : Form
{
private readonly Rectangle hitbox = new Rectangle(50, 50, 10, 10);
private readonly Pen pen = new Pen(Brushes.Black);
public Form1()
{
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(pen, hitbox);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if ((e.X > hitbox.X) && (e.X < hitbox.X + hitbox.Width) &&
(e.Y > hitbox.Y) && (e.Y < hitbox.Y + hitbox.Height))
{
Text = "HIT";
}
else
{
Text = "NO";
}
}
}
Rectangle has several handy but often overlooked functions. In this case using the Rectangle.Contains(Point) function is the best solution:
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (hitbox.Contains(e.Location)) .. // clicked inside
}
To determine if you clicked on the outline you will want to decide on a width, since the user can't easily hit a single pixel.
For this you can use either GraphicsPath.IsOutlineVisible(Point)..
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
GraphicsPath gp = new GraphicsPath();
gp.AddRectanle(hitbox);
using (Pen pen = new Pen(Color.Black, 2f))
if (gp.IsOutlineVisible(e.location), pen) .. // clicked on outline
}
..or stick to rectangles..:
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Rectangle inner = hitbox;
Rectangle outer = hitbox;
inner.Inflate(-1, -1); // a two pixel
outer.Inflate(1, 1); // ..outline
if (outer.Contains(e.Location) && !innerContains(e.Location)) .. // clicked on outline
}

Partial screenshot with rectangle of selection c#

I'm trying to take a partial screenshot of the screen and put it in a picturebox. The user has to draw a rectangle and then i have to get the portion inside the rectangle.
protected override void OnMouseDown(MouseEventArgs e)
{
mRect = new Rectangle(e.X, e.Y, 0, 0);
this.Invalidate();
}
protected override void OnMouseMove(MouseEventArgs e)
{
if( e.Button == MouseButtons.Left)
{
mRect = new Rectangle(mRect.Left, mRect.Top, e.X - mRect.Left, e.Y - mRect.Top);
this.Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
using(Pen pen = new Pen(Color.Red, 1))
{
e.Graphics.DrawRectangle(pen, mRect);
}
}
With this code i can draw a recangle, but i don't know how to do the screenshot. Maybe with the fonction CopyToScreen?
If someone have an idea?
Thanks!
Here is some code I use - it grabs the whole desktop
public static Bitmap GetScreenAsBmp()
{
int min_x=0, max_x=0, min_y=0, max_y=0;
foreach (Screen s in Screen.AllScreens)
{
min_x = System.Math.Min(min_x, s.Bounds.Left);
max_x = System.Math.Max(max_x, s.Bounds.Right);
min_y = System.Math.Min(min_y, s.Bounds.Top);
max_y = System.Math.Max(max_y, s.Bounds.Bottom);
}
Bitmap bmp = new Bitmap(max_x - min_x, max_y - min_y);
Graphics dst_dc = Graphics.FromImage(bmp);
dst_dc.CopyFromScreen(min_x, min_y, 0, 0, new Size(max_x - min_x, max_y - min_y));
dst_dc.Dispose();
return bmp;
}
This is a complete solution for the problem.
It is triggered by a Button and then:
creates and styles a second Form which
is transparent without borders etc and fills the screen
has all events needed to draw a Rectangle
copies the screen after the rectangle is drawn, i.e. after the MouseUp
and copies the Bitmap into the Image of a PictureBox on the 1st Form.
Finally it closes the 2nd form. During the screencopy process both windows are hidden, so you can grab the screen itself with all its content.
Note that many video overlays can't be captured this way!
Note that this code always captures from the 1st screen!
For best usability the users should somehow be informed that he is expected to draw the frame after the Button is pressed and the window has disappeared!
To use it simply add a Button and a PictureBox to your Form..
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Form form2;
Point MD = Point.Empty;
Rectangle rect = Rectangle.Empty;
private void button2_Click(object sender, EventArgs e)
{
Hide();
form2 = new Form();
form2.BackColor = Color.Wheat;
form2.TransparencyKey = form2.BackColor;
form2.ControlBox = false;
form2.MaximizeBox = false;
form2.MinimizeBox = false;
form2.FormBorderStyle = FormBorderStyle.None;
form2.WindowState = FormWindowState.Maximized;
form2.MouseDown += form2_MouseDown;
form2.MouseMove += form2_MouseMove;
form2.Paint += form2_Paint;
form2.MouseUp += form2_MouseUp;
form2.Show();
}
void form2_MouseDown(object sender, MouseEventArgs e)
{
MD = e.Location;
}
void form2_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button !=MouseButtons.Left) return;
Point MM = e.Location;
rect = new Rectangle(Math.Min(MD.X, MM.X), Math.Min(MD.Y, MM.Y),
Math.Abs(MD.X - MM.X), Math.Abs(MD.Y - MM.Y));
form2.Invalidate();
}
void form2_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.Red, rect);
}
void form2_MouseUp(object sender, MouseEventArgs e)
{
form2.Hide();
Screen scr = Screen.AllScreens[0];
Bitmap bmp = new Bitmap(rect.Width, rect.Height);
using (Graphics G = Graphics.FromImage(bmp))
{
G.CopyFromScreen(rect.Location, Point.Empty, rect.Size,
CopyPixelOperation.SourceCopy);
pictureBox1.Image = bmp;
}
form2.Close();
Show();
}
}

Draw a fill rectangle dynamically on screen in C#

I would like to draw a fill rectangle dynamically on my screen, with an opacity at 0.1. The problem is that when i move the mouse, the previous rectangles aren't clear.
This is the drawing methods.
private void OnMouseDown(object sender, MouseEventArgs e)
{
isMouseDown = true;
x = e.X;
y = e.Y;
g = this.selectorForm.CreateGraphics();
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (!isMouseDown) return;
this.selectorForm.Invalidate();
g.FillRectangle(brush, this.getRectangle(x, y, e.X, e.Y));
g.DrawRectangle(pen, this.getRectangle(x, y, e.X, e.Y));
}
This is my selectorForm
internal class SelectorForm : Form
{
protected override void OnPaintBackground(PaintEventArgs e)
{
}
}
An example when I draw a rectangle (several overlapping rectangles)
And Invalidate() doesn't work because I override OnPaintBackground. But if I don't do this override, when I do this.selectorForm.Show(), my screen becomes gray.
So how can I draw a rectangle with an opacity 0.1 on my screen?
Thank you !
This is an example that works for me.
The crucial parts are:
using the Paint event and its Graphics
adding a Clear(BackgroundColor) or else I get the same artifacts you see
for transparency the TransparencyKey property should be used. There is a certain choice of colors:
Common colors may conflict with other things on the Form
Fuchsia works if you want to click through the Form
Other not so common colors are suitable; I chose a light green
You may want to adapt to your way of setting up events, I just did it this way for faster testing.
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
DoubleBuffered = true;
Opacity = 0.1f;
// a color that will allow using the mouse on the form:
BackColor = Color.GreenYellow;
TransparencyKey = BackColor;
}
Point mDown = Point.Empty;
Point mCur = Point.Empty;
private void Form2_MouseDown(object sender, MouseEventArgs e)
{
mDown = e.Location;
}
private void Form2_MouseUp(object sender, MouseEventArgs e)
{
mDown = Point.Empty;
}
private void Form2_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
mCur = e.Location;
Invalidate();
}
private void Form2_Paint(object sender, PaintEventArgs e)
{
if (mDown == Point.Empty) return;
Size s = new System.Drawing.Size(Math.Abs(mDown.X - mCur.X),
Math.Abs(mDown.Y - mCur.Y) );
Point topLeft = new Point(Math.Min(mDown.X, mCur.X),
Math.Min(mDown.Y, mCur.Y));
Rectangle r = new Rectangle(topLeft, s);
e.Graphics.Clear(this.BackColor); // <--- necessary!
e.Graphics.FillRectangle(Brushes.Bisque, r ); // <--- pick your..
e.Graphics.DrawRectangle(Pens.Red, r); // <--- colors!
}
}
}
You can try following code:
g.Clear();
g.FillRectangle(brush, this.getRectangle(x, y, e.X, e.Y));
g.DrawRectangle(pen, this.getRectangle(x, y, e.X, e.Y));

In a PictureBox, drawing a temporary circle on mouse down, to follow the mouse, until the mouseUp event is detected

I'm really new to drawing in C#, and I'm using Windows Forms instead of WPF, so maybe I'm doing it wrong from the get-go... you tell me... but I'd like to have a temporary marker put down on the PictureBox (on MouseDown) that will follow the mouse (erasing the previous drawings of itself, i.e. not leaving a trail), and then being drawn in the final position on the MouseUp event.
Here's some skeleton code that you guys can fill in:
bool mDown;
Graphics g; // initialized to pictureBox1.CreateGraphics() on Form_Load, though
// I am unsure how that differs from Graphics.FromImage(pictureBox1)
SolidBrush sbGray, sbGreen;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
mDown = true;
// store/push initial drawing
g.FillEllipse(sbGray, e.X - 5, e.Y - 5, 10, 10);
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (mDown)
{
// restore/pop initial drawing, erasing old trail
g.FillEllipse(sbGray, e.X - 5, e.Y - 5, 10, 10);
}
}
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
// restore/pop initial drawing, erasing old trail
g.FillEllipse(sbGreen, e.X - 5, e.Y - 5, 10, 10);
mDown = false;
}
I suppose there are several ways to skin a cat, such as changing the mouse icon, so perhaps this is not even the best way to do it? Even so, I will probably need to know the answers to both questions -- whether there is a better way to do it, and also how to extract the graphics from a PictureBox (as this knowledge will most likely prove useful later anyways.)
(Note: Although the gray circles should erase themselves, the green circles should be persistent and multiple green circles should be capable of existing in the PictureBox at the same time.)
You need to look at the PictureBox's Paint Event also, it is better to do all of your graphics in the Paint event since you do not have to worry about disposing the Graphic Object.. See if this is what you were wanting.
Edit: added code to retain points and also to clear them.
public partial class Form1 : Form
{
bool mDown;
SolidBrush sbGray = new SolidBrush(Color.Gray);
SolidBrush sbGreen = new SolidBrush(Color.LimeGreen);
SolidBrush sbTemp;
Point mPosition = new Point();
public List<Point> points = new List<Point>();
public Form1()
{
InitializeComponent();
pictureBox1.Image = Image.FromFile(#"C:\Temp\Test.jpg");
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mDown = true;
mPosition = new Point(e.X, e.Y);
sbTemp = sbGray;
pictureBox1.Invalidate();
}
else
{
points.Clear();
sbTemp = null;
pictureBox1.Invalidate();
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (mDown)
{
mPosition = new Point(e.X, e.Y);
sbTemp = sbGray;
pictureBox1.Invalidate();
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mPosition = new Point(e.X, e.Y);
points.Add(mPosition);
sbTemp = sbGreen;
pictureBox1.Invalidate();
mDown = false;
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (sbTemp != null)
{
e.Graphics.FillEllipse(sbTemp, mPosition.X -5, mPosition.Y -5, 10, 10);
}
if (points != null)
{
foreach (var loc in points)
{
e.Graphics.FillEllipse(sbGreen, loc.X - 5, loc.Y - 5, 10, 10);
}
}
}
}

C# graphics flickering

I am working on kind of drawing program but I have a problem with flickering while moving a mouse cursor while drawing a rubberband line. I hope you can help me to remove that flickering of line, here is the code:
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 GraphicsTest
{
public partial class Form1 : Form
{
int xFirst, yFirst;
Bitmap bm = new Bitmap(1000, 1000);
Graphics bmG;
Graphics xG;
Pen pen = new Pen(Color.Black, 1);
bool draw = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
bmG = Graphics.FromImage(bm);
xG = this.CreateGraphics();
bmG.Clear(Color.White);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
xFirst = e.X;
yFirst = e.Y;
draw = true;
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
bmG.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
draw = false;
xG.DrawImage(bm, 0, 0);
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (draw)
{
xG.DrawImage(bm, 0, 0);
xG.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
xG.DrawImage(bm, 0, 0);
}
}
}
First don't use CreateGraphics() unless you absolutely have to. Bind an event handler to OnPaint and call Invalidate() when you want to refresh the surface.
If you don't want it to flicker you'll need to double buffer your drawing surface. The easiest way to do this is to set your form's DoubleBuffered property to True.
I would highly recommend if you plan on extending this to do your drawing to the PictureBox control. PictureBox is double-buffered by default and allows you to control your drawing region much more simply.
In code:
public partial class Form1 : Form
{
int xFirst, yFirst;
Bitmap bm = new Bitmap(1000, 1000);
Graphics bmG;
Pen pen = new Pen(Color.Black, 1);
bool draw = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
bmG = Graphics.FromImage(bm);
bmG.Clear(Color.White);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
xFirst = e.X;
yFirst = e.Y;
draw = true;
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
bmG.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
draw = false;
Invalidate();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (draw)
{
Invalidate();
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (draw) {
e.Graphics.DrawImage(bm, 0, 0);
e.Graphics.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
} else {
e.Graphics.DrawImage(bm, 0, 0);
}
}
}
Edit:
Another issue, you are creating a private Pen member. Pens (and Brushes, as well as many GDI+ objects) represent handles to unmanaged objects that need to be disposed otherwise your program will leak. Either wrap them in using statements (the preferred and exception-safe way) or explicitly dispose of them in the form's Dispose method.
Alternatively in System.Drawing you can access some pre-built Pens and Brushes that don't need to be (and shouldn't be) disposed. Use them like:
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (draw) {
e.Graphics.DrawImage(bm, 0, 0);
e.Graphics.DrawLine(Pens.Black, xFirst, yFirst, e.X, e.Y);
} else {
e.Graphics.DrawImage(bm, 0, 0);
}
}
The reason it is flickering is that you are drawing the background (which is immediately displayed on screen, wiping away the line) and then superimpose the line. Thus the line keeps disappearing and appearing, giving a flickering display.
The best solution to this is called Double Buffering. What you do is draw the whole image to an "offscreen" bitmap, and only show it on the screen when it is completed. Because you only ever display the completed image, there is no flickering effect. You should just be able to set this.DoubleBuffered = true to get WinForms to do all the hard work for you.
NB: You shouldn't really be drawing outside of your paint handler - ideally, you should Invalidate() the area that needs redrawing, and then your paint handler will redraw just that area (with any overlaid lines etc as needed).
Fixed and working code.
public partial class Form1 : Form
{
int x1, y1, x2, y2;
bool drag = false;
Bitmap bm = new Bitmap(1000, 1000);
Graphics bmg;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
bmg = Graphics.FromImage(bm);
}
private void pictureBox_MouseDown(object sender, MouseEventArgs e)
{
drag = true;
x1 = e.X;
y1 = e.Y;
}
private void pictureBox_MouseUp(object sender, MouseEventArgs e)
{
drag = false;
bmg.DrawLine(Pens.Black, x1, y1, e.X, e.Y);
pictureBox.Invalidate();
}
private void pictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (drag)
{
x2 = e.X;
y2 = e.Y;
pictureBox.Invalidate();
}
}
private void pictureBox_Paint(object sender, PaintEventArgs e)
{
if (drag) {
e.Graphics.DrawImage(bm, 0, 0);
e.Graphics.DrawLine(Pens.Black, x1, y1, x2, y2);
}
else {
e.Graphics.DrawImage(bm, 0, 0);
}
}
}
I Use this to manage the double buffering into a panel:
myPanel.GetType().GetMethod("SetStyle",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic).Invoke(myPanel,
new object[]
{
System.Windows.Forms.ControlStyles.UserPaint |
System.Windows.Forms.ControlStyles.AllPaintingInWmPaint |
System.Windows.Forms.ControlStyles.DoubleBuffer, true
});

Categories