In the last few months I've been working on an application, and one of it's functions is that it can crop images. So, I've coded a function that draws a transparent orange rectangle, to show the user the crop area, but it works very slowly - can anyone help me/show me a way to make it faster?
Here is the code:
Image source;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e) {
mousePos = e.Location;
}
Point mousePos;
private void pictureBox1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
Image editSource = new Bitmap(source);
Graphics g = Graphics.FromImage(editSource);
SolidBrush brush = new SolidBrush(
Color.FromArgb(128, Color.Orange.R, Color.Orange.G, Color.Orange.B));
int width = e.X - mousePos.X;
if (width < 0) {
width *= -1;
}
int height = e.Y - mousePos.Y;
if (height < 0) {
height *= -1;
}
Size cropRectSize = new Size(width, height);
Rectangle cropRect = new Rectangle(mousePos, cropRectSize);
g.FillRectangle(brush, cropRect);
pictureBox1.Image = editSource;
}
}
The way to make it faster is don't create bitmaps on mouse move. If you need to draw on a graphics surface, do so without creating new bitmaps.
Don't use a picture box. Add your own user drawn control.
On MouseMove just invalidate the changed region
In Draw, write directly to graphics object, don't play with in memory bitmaps
So, all recommendations for not using picture box aside... I'll give you a method to doing it ;)
The idea behind this is to only use mouse move, mouse down, etc to store what should be drawn. Then when it's time to draw it use the stored state. This draws an orange rectangle whenever you have the mouse depressed on the picture box (even though recommendation is to not use picture box this same approach can be used for other surfaces.).
Point startPoint;
Point currentPoint;
bool draw = false;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
startPoint = e.Location;
draw = true;
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
draw = false;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
currentPoint = e.Location;
pictureBox1.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (draw)
{
int startX = Math.Min(startPoint.X, currentPoint.X);
int startY = Math.Min(startPoint.Y, currentPoint.Y);
int endX = Math.Max(startPoint.X, currentPoint.X);
int endY = Math.Max(startPoint.Y, currentPoint.Y);
e.Graphics.DrawRectangle(Pens.Orange, new Rectangle(startX, startY, endX-startX, endY-startY));
}
}
Related
Hey so I'm currently using this post to create rectangles on a canvas and was wandering how i could make the drawn rectangle 2px larger than the actual rectangle recorded. Is this possible and if so how would i do this have been trying to work it out for a while now would be really useful to finally have a solution.
Point startPos; // mouse-down position
Point currentPos; // current mouse position
bool drawing; // busy drawing
List<Rectangle> rectangles = new List<Rectangle>(); // previous rectangles
private Rectangle getRectangle() {
return new Rectangle(
Math.Min(startPos.X, currentPos.X),
Math.Min(startPos.Y, currentPos.Y),
Math.Abs(startPos.X - currentPos.X),
Math.Abs(startPos.Y - currentPos.Y));
}
private void canevas_MouseDown(object sender, MouseEventArgs e) {
currentPos = startPos = e.Location;
drawing = true;
}
private void canevas_MouseMove(object sender, MouseEventArgs e) {
currentPos = e.Location;
if (drawing) canevas.Invalidate();
}
private void canevas_MouseUp(object sender, MouseEventArgs e) {
if (drawing) {
drawing = false;
var rc = getRectangle();
if (rc.Width > 0 && rc.Height > 0) rectangles.Add(rc);
canevas.Invalidate();
}
}
private void canevas_Paint(object sender, PaintEventArgs e) {
if (rectangles.Count > 0) e.Graphics.DrawRectangles(Pens.Black, rectangles.ToArray());
if (drawing) e.Graphics.DrawRectangle(Pens.Red, getRectangle());
}
just for reference this code came from this post
https://stackoverflow.com/questions/4060446/how-to-draw-rectangle-on-mousedown-move-c-sharp
In the getRectangle method you just need to change the values that you use in the constructor for the Rectangle. Since the third one refers to the width property, you add the number of pixels to Math.Abs(startPos.X - currentPos.X).After this the check if (rc.Width > 0) is useless for obvious reasons.
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));
I have to display a picture (in my case a picture of some cars), and have to select the cars appearing in the current frame with a rectangle, I want to enable selecting a rectangle around a car, enable deleting a rectangle, if possible moving it to the sides also.
I tried to use Graphics on my pictureBox, but I don't know how to clear a rectangle when I have custom background (the picture itself, and not a solid color).
I asked for a headlines and not a full code (but if there is so, I would like to get), I am new with C# here is what I managed to write, this only draws at the end of the selection and doesn't reflect anything while doing the selection itself:
private void prevPictureBox_MouseDown_1(object sender, MouseEventArgs e){
Point startPoint = new Point(e.X, e.Y); //
if (e.Button == MouseButtons.Left)
{
currRect = new Rectangle();
currRect.X = startPoint.X;
currRect.Y = startPoint.Y;
isDrag = true;
}
}
private void prevPictureBox_MouseMove(object sender, MouseEventArgs e) {
if (isDrag) {
endPoint = new Point(e.X, e.Y);
currRect.Width = endPoint.X - startPoint.X;
currRect.Height = endPoint.Y - startPoint.Y;
}
}
private void prevPictureBox_MouseUp(object sender, MouseEventArgs e)
{
isDrag = false;
graphics = this.prevPictureBox.CreateGraphics();
graphics.DrawRectangle(new Pen(Brushes.Red), currRect.X, currRect.Y, currRect.Width, currRect.Height);
}
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);
}
}
}
}
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));
}