Drawing rectangle on picturebox - how to limit area of rectangle? - c#

i'm drawing rectangle on picturebox with mouse events:
private void StreamingWindow_MouseDown(object sender, MouseEventArgs e)
{
rect = new Rectangle(e.X, e.Y, 0, 0);
this.Invalidate();
}
private void StreamingWindow_Paint(object sender, PaintEventArgs e)
{
if (painting == true)
{
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, rect);
}
}
}
private void StreamingWindow_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// Draws the rectangle as the mouse moves
rect = new Rectangle(rect.Left, rect.Top, e.X - rect.Left, e.Y - rect.Top);
}
this.Invalidate();
}
After drawing rectangle i can capture inside of it, and save as jpg.
What's my problem?
I can draw retangle which borders are outside area of picturebox:
How can i limit area of rectangle that border of picturebox is max allowed location of rectangle?
Sorry for my english, i hope you'll understand my problem :)
So as a result i'd like to have something like this:

private void StreamingWindow_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// Draws the rectangle as the mouse moves
rect = new Rectangle(rect.Left, rect.Top, Math.Min(e.X - rect.Left, pictureBox1.ClientRectangle.Width - rect.Left), Math.Min(e.Y - rect.Top, pictureBox1.ClientRectangle.Height - rect.Top));
}
this.Invalidate();
}

Why not setting the rect coords to something
rect = new Rectangle(min(e.X, pictureBoxX), min(e.Y, pictureBoxY), 0, 0);
You need to calculate pictureX and pictureY according to the location and position of the picture box.

I would say the easiest way to achieve this, and I personally think more natural one from UX perspective, is: after MouseUp check if BottomLeft corner of rectangle is outside of area of picture box, if so, just bring it "back" and align it to the picture box's angle just as you drawed.
EDIT
Just to give you an idea what I'm talking about, a pseudocode
private void StreamingWindow_MouseUp(object sender, MouseEventArgs e)
{
if(rect.Right > myPictureBox.ClientRectangle.Right)
{
rect.Width = myPictureBox.Right - rect.Left - someoffset;
}
if(rect.Bottom > myPictureBox.ClientRectangle.Bottom)
{
rect.Height= myPictureBox.Bottom - rect.Top - someoffset;
}
}
Something like this. But you need to check this.

Another way for solving this is preventing rectangle to be drown outside of pictureBox control
private void StreamingWindow_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (e.X < StreamingWindow.Width && Math.Abs(e.Y) < StreamingWindow.Height)
// Draws the rectangle as the mouse moves
rect = new Rectangle(rect.Left, rect.Top, e.X - rect.Left, e.Y -rect.Top);
}
this.Invalidate();
}
Somebody can find this solution more useful

Related

Draw rectangle with negative coordinates

When i'm trying to draw a rectangle in PictureBox with negative coordinates (-x and -y) the rectangle dissapears, though when it has positive coordinates everything is okay. Here's the code:
Here I get starting coordinates of rectangle
private void PictureBox1_MouseDown(object sender, MouseEventArgs e)
{
start_point.X = e.X;
start_point.Y = e.Y;
}
Here I get the ending coordinates of rectangle:
private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
end_point.X = e.X;
end_point.Y = e.Y;
PictureBox1.Refresh();
}
}
Here I calculate the rectangles width and height:
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.FillRectangle(sb, start_point.X, start_point.Y, end_point.X - start_point.X, end_point.Y - start_point.Y);
}
If the starting point coordinates are smaller than ending ones, everything works just fine, but when the ending coordinates are smaller than starting ones, the width or height or both values are negative...
How can I solve this problem?
There are 4 possible ways for the user to drag the mouse to make the rectangle. Only one of them you're happy with right now, from upper-left to lower-right. The other 3 ways produce negative values for the rectangle's Width or Height. You deal with all 4 possibilities like this:
var rc = new Rectangle(
Math.Min(startpoint.x, endpoint.x),
Math.Min(startpoint.y, endpoint.y),
Math.Abs(endpoint.x - startpoint.x),
Math.Abs(endpoint.y - startpoint.y));
e.Graphics.FillRectangle(sb, rc);
If the starting X is < the ending X, just swap the values before drawing. Same for the Y coordinates.
if ( start_point.X < end_point.X )
{
var oldX = start_point.X;
start_point.X = end_point.X;
end_point.X = oldX;
}
if ( start_point.Y < end_point.Y )
{
var oldY = start_point.Y;
start_point.Y = end_point.Y;
end_point.Y = oldY;
}

Blur the selected part of the image

I am trying to create an application in C# (WinForms), something similar to this iOS Question
I managed to get part of it working, I can blur an image using this algorithm
Also I am able to draw a selection rectangle, I do not know if I am going wrong with the blurring or passing the rectangle. I have attached the file as shown below.
As seen, the blurring is outside the selection box.
I have pasted the code below:
// Start Rectangle
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
// Determine the initial rectangle coordinates...
RectStartPoint = e.Location;
Invalidate();
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
Point tempEndPoint = e.Location;
Rect.Location = new Point(
Math.Min(RectStartPoint.X, tempEndPoint.X),
Math.Min(RectStartPoint.Y, tempEndPoint.Y));
Rect.Size = new Size(
Math.Abs(RectStartPoint.X - tempEndPoint.X),
Math.Abs(RectStartPoint.Y - tempEndPoint.Y));
pictureBox1.Invalidate();
}
// Draw Area
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
// Draw the rectangle...
if (pictureBox1.Image != null)
{
if (Rect != null && Rect.Width > 0 && Rect.Height > 0)
{
e.Graphics.DrawRectangle(selectionPen, Rect);
}
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
//Right now I am using right click as a call to blur
if (e.Button == MouseButtons.Right)
{
if (Rect.Contains(e.Location))
{
pictureBox1.Image = Blur(pictureBox1.Image, Rect, 5);
pictureBox1.Refresh();
}
}
}
private void blurPageToolStripMenuItem_Click(object sender, EventArgs e)
{
FullRect = new Rectangle(0, 0, pictureBox1.Image.Width, pictureBox1.Image.Height);
pictureBox1.Image = Blur(pictureBox1.Image, FullRect, 5);
}
private System.Drawing.Image Blur(System.Drawing.Image image, Rectangle rectangle, Int32 blurSize)
{
Bitmap blurred = new Bitmap(image); //image.Width, image.Height);
using (Graphics graphics = Graphics.FromImage(blurred))
{
// look at every pixel in the blur rectangle
for (Int32 xx = rectangle.Left; xx < rectangle.Right; xx += blurSize)
{
for (Int32 yy = rectangle.Top; yy < rectangle.Bottom; yy += blurSize)
{
Int32 avgR = 0, avgG = 0, avgB = 0;
Int32 blurPixelCount = 0;
Rectangle currentRect = new Rectangle(xx, yy, blurSize, blurSize);
// average the color of the red, green and blue for each pixel in the
// blur size while making sure you don't go outside the image bounds
for (Int32 x = currentRect.Left; (x < currentRect.Right && x < image.Width); x++)
{
for (Int32 y = currentRect.Top; (y < currentRect.Bottom && y < image.Height); y++)
{
Color pixel = blurred.GetPixel(x, y);
avgR += pixel.R;
avgG += pixel.G;
avgB += pixel.B;
blurPixelCount++;
}
}
avgR = avgR / blurPixelCount;
avgG = avgG / blurPixelCount;
avgB = avgB / blurPixelCount;
// now that we know the average for the blur size, set each pixel to that color
graphics.FillRectangle(new SolidBrush(Color.FromArgb(avgR, avgG, avgB)), currentRect);
}
}
graphics.Flush();
}
return blurred;
}
Another problem I am facing is, when the form is loaded initially, it starts in minimised mode, now if I use the selection (red rectangle), and then if I maximise the application the selected part of the picture is different.
Thank you for the help/suggestion in advance. If any links to a tool similar is around, please do share as I might have missed it. Thanks
You may be experiencing this issue because your image is stretched in the PictureBox. You can verify this is the issue by setting the SizeMode property of the PictureBox to Normal.
This is the sequence of events:
The selection rectangle is drawn.
The point/rectangle for the selection is determined.
The unstretched image is retrieved from the PictureBox, and is passed to the Blur method, with the calculated rectangle.
The unstretched image is blurred over the area described by the rectangle.
The unstretched image, now blurred, is assigned to the PictureBox.
The PictureBox stretches the image out to according to its SizeMode setting.
This makes the image appear to have blurred in a different location than what you selected.
The code you have looks at the selection rectangle, and uses those points to operate on the original image, not the stretched version of the image. If you want to blur the stretched image, you will need to get the stretched image first, then apply the blur to the rectangle selected on that image. Here is an example:
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
//Right now I am using right click as a call to blur
if (e.Button == MouseButtons.Right)
{
if (Rect.Contains(e.Location))
{
pictureBox1.Image = Blur(getPictureBoxImage(), Rect, 5);
pictureBox1.Refresh();
}
}
}
private Bitmap getPictureBoxImage()
{
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(pictureBox1.Image,
new Rectangle(0, 0, bmp.Width, bmp.Height));
}
return bmp;
}
Code for retrieving the stretched image is derived from this answer: https://stackoverflow.com/a/8702405/935052

Drawn shape/line fires on MouseEnter

i have PictureBox, how can I draw a shape/line that fires on MouseEnter event and change color or do more.
private void ImgViewer_Paint(object sender, PaintEventArgs e)
{
var graph = e.Graphics;
using (var pen = new Pen(Color.FromArgb(0, 255, 0)))
graph.DrawLine(pen, x1, y1, x2, y2);
}
this code is not enough, i guess
If you know the equation of the shape you could calculate whether the mouse is within or outside the shape area. Note that this is easy if the shape is consisted of the straight lines or circles (ellipses) for which the geometrical equations are relatively simple. For instance if your shape is a triangle with x and y coordinates (10,10), (50,10) and (30,50) than you should derive the equations of the lines using the equation of the line in two points:
y-y1 = ((y2-y1)/(x2-x1))*(x-x1)
the equations of the lines of our triangle would be:
y=1
y=2*x-10
y=-2*x+110
We should draw that triangle on some canvas, let's say on the PictureBox with FixedSingle border. Add the Paint event handler
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Point[] p = new Point[3];
p[0] = new Point(10,10);
p[1] = new Point(50,10);
p[2] = new Point(30,50);
e.Graphics.DrawLines(Pens.Black, p);
e.Graphics.FillPolygon(Brushes.Red, p);
}
We should add the MouseMove event handler for the PictureBox
bool inside = false;
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Y > 10 && e.Y < 2 * e.X - 10 && e.Y < -2 * e.X + 110)
{
if (!inside)
{
inside = true;
HandleMouseEnter();
}
}
else
inside = false;
}
void HandleMouseEnter()
{
MessageBox.Show("Mouse inside");
}
In if statement whether the mouse cursor is within the triangle (note that the coordinate origin in C# is on the top-left corner but it is similar to the real geometry). The HandleMouseEnter is the method that handles the mouse enter.
You could use similar approach for an arbitrary shape but you should have geometry equations that describe it.

Calculate angle while rotating rectangle

I have a program in C# (Windows Forms) which has a rectangle on a Picture Box. They can be drawn at an angle too (rotated). I want to rotate that rectangle using my mouse movements.
I have the code for moving that rectangle
Rectangle areaRect = new Rectangle(100,100, 300, 300);
Bool dragging = false;
Point ptOld = new Point(0, 0);
protected override void OnPaint(PaintEventArgs e)
{
Graphics dcPaint = e.Graphics;
dcPaint.DrawRectangle(rectPen, areaRect);
}
protected override void OnMouseDown(MouseEventArgs e)
{
ptOld = new Point(e.X, e.Y);
dragging = true;
}
protected override void OnMouseMove(MouseEventArgs e)
{
if(dragging = true)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
int dy = ptNew.Y - ptOld.Y;
areaRect.Offset(dx, dy); // This one moves the rectangle
ptOld = ptNew;
this.Invalidate();
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
dragging = false;
}
Now My requirement is to rotate this rectangle, Any idea, how that can be achieved.
I think you want to calculate angle between two points on X-axis. If so, try the following code:
const double Rad2Deg = 180.0 / Math.PI;
return Math.Atan2(ptOld.Y - e.Y, e.X - ptOld.X) * Rad2Deg;
Also check out this article on calculating angle between two points
When rotating rectangle with mouse, you define the center of rotation (centerXY), in you case it will be the center of the rectangle maybe.
On mouse down record mouse coordinates, mouse_downXY. These two points define a base line. When moving mouse you'll define another line, formed by current mouse coordinates and the rectangle center.
So you'll need to compute the angle between line (centerXY, mouse_downXY) and (centerXY, current_mouseXY). Computing angle between 2 lines with knowing 3 points coordinates is simple trigonometry, so I won't write code for you :) However this post has the answer.
You can calculate the angle using the difference between the old and the new x mouse coordinate (dx in your example). You can use the RotateTransform method of the Graphics object to rotate the rectangle.
I modified your code to do the rotation in addition to the translation. You can move the rectangle with the left mouse button and you can rotate it using the right mouse button.
Rectangle areaRect = new Rectangle(100, 100, 300, 300);
bool dragging = false;
bool rotating = false;
Point ptOld = new Point(0, 0);
float angle = 0;
protected override void OnPaint(PaintEventArgs e)
{
Graphics dcPaint = e.Graphics;
dcPaint.RotateTransform(angle);
dcPaint.DrawRectangle(Pens.Black, areaRect);
dcPaint.RotateTransform(-angle);
}
protected override void OnMouseDown(MouseEventArgs e)
{
ptOld = new Point(e.X, e.Y);
if (e.Button == MouseButtons.Left)
{
dragging = true;
}
if (e.Button == MouseButtons.Right)
{
rotating = true;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (dragging == true)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
int dy = ptNew.Y - ptOld.Y;
areaRect.Offset(dx, dy); // This one moves the rectangle
ptOld = ptNew;
this.Invalidate();
}
if (rotating == true)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
angle = angle + dx / 10f;
ptOld = ptNew;
this.Invalidate();
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
dragging = false;
rotating = false;
}
Right now, the rectangle is rotated around its top left corner. If you apply a translation before the rotation, you can get it to rotate around its middle.

C# Transparent solid rectangle drawn on a 50% opaque form

I've been trying to mimic the Windows 7 Snipping Tool in how it overlays the screen with a semi-transparent gray layer which becomes completely transparent inside the selection area. I've come pretty close. I'm displaying a borderless 50% opaque gray form which covers the entire screen and has a TransparencyKey of Fuchsia. Then on top of that form I draw 2 rectangles. A solid fuchsia rectangle for the transparency and another rectangle for the red border. It works, but only if I do one of three things, none of which are options.
Disable double buffering which makes the form flicker while drawing
Change the desktop color mode to 16bit from 32bit
Make the form 100% opaque
Here is my code. Any suggestion on how to get this to work?
public partial class frmBackground : Form
{
Rectangle rect;
public frmBackground()
{
InitializeComponent();
this.MouseDown += new MouseEventHandler(frmBackground_MouseDown);
this.MouseMove += new MouseEventHandler(frmBackground_MouseMove);
this.Paint += new PaintEventHandler(frmBackground_Paint);
this.DoubleBuffered = true;
this.Cursor = Cursors.Cross;
}
private void frmBackground_MouseDown(object sender, MouseEventArgs e)
{
Bitmap backBuffer = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
rect = new Rectangle(e.X, e.Y, 0, 0);
this.Invalidate();
}
private void frmBackground_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
rect = new Rectangle(rect.Left, rect.Top, e.X - rect.Left, e.Y - rect.Top);
this.Invalidate();
}
private void frmBackground_Paint(object sender, PaintEventArgs e)
{
Pen pen = new Pen(Color.Red, 3);
e.Graphics.DrawRectangle(pen, rect);
SolidBrush brush = new SolidBrush(Color.Fuchsia);
e.Graphics.FillRectangle(brush, rect);
}
}
you can use
Brush brush = new SolidBrush(Color.FromArgb(alpha, red, green, blue))
where alpha goes from 0 to 255, so a value of 128 for your alpha will give you 50% opactity.
this solution was found in this question

Categories