PictureBox Zoom Mode Effect With Graphics Object - c#

When you load an image into a PictureBox there is a zoom for the image placement, how do I achieve that same effect with a graphics object?

I think you mean you want to draw some Image yourself in a Rectangle and using some Graphics object like as the PictureBox renders its Image in Zoom mode. Try the following code. I suppose you want to draw the Image on a Form, the drawing code should be added in a Paint event handler of your form:
//The Rectangle (corresponds to the PictureBox.ClientRectangle)
//we use here is the Form.ClientRectangle
//Here is the Paint event handler of your Form1
private void Form1_Paint(object sender, EventArgs e){
ZoomDrawImage(e.Graphics, yourImage, ClientRectangle);
}
//use this method to draw the image like as the zooming feature of PictureBox
private void ZoomDrawImage(Graphics g, Image img, Rectangle bounds){
decimal r1 = (decimal) img.Width/img.Height;
decimal r2 = (decimal) bounds.Width/bounds.Height;
int w = bounds.Width;
int h = bounds.Height;
if(r1 > r2){
w = bounds.Width;
h = (int) (w / r1);
} else if(r1 < r2){
h = bounds.Height;
w = (int) (r1 * h);
}
int x = (bounds.Width - w)/2;
int y = (bounds.Height - h)/2;
g.DrawImage(img, new Rectangle(x,y,w,h));
}
To test it on your form perfectly, you should also have to set ResizeRedraw = true and enable DoubleBuffered:
public Form1(){
InitializeComponent();
ResizeRedraw = true;
DoubleBuffered = true;
}

Related

Get pixel value of image draw through onPaint methode

I am developping a custom C# UserControl WinForm to display an image on background and display scrollbars when I zoom with the mouse. For this, I overrided the OnPaint method. In it, if I have an image loaded, according some parameters I know the source and destination rectangle sizes. In the same way, I know what scale and translation apply to always keeping the top left corner on screen when zooming. And for the zoom, I use the scrollmouse event to update the zoom factory.
Here is my code related to this override method.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Draw image
if(image != null)
{
//
Rectangle srcRect, destRect;
Point pt = new Point((int)(hScrollBar1.Value/zoom), (int)(vScrollBar1.Value/zoom));
if (canvasSize.Width * zoom < viewRectWidth && canvasSize.Height * zoom < viewRectHeight)
srcRect = new Rectangle(0, 0, canvasSize.Width, canvasSize.Height); // view all image
else if (canvasSize.Width * zoom < viewRectWidth)
srcRect = new Rectangle(0, pt.Y, canvasSize.Width, (int)(viewRectHeight / zoom)); // view a portion of image but center on width
else if (canvasSize.Height * zoom < viewRectHeight)
srcRect = new Rectangle(pt.X, 0, (int)(viewRectWidth / zoom), canvasSize.Height); // view a portion of image but center on height
else
srcRect = new Rectangle(pt, new Size((int)(viewRectWidth / zoom), (int)(viewRectHeight / zoom))); // view a portion of image
destRect = new Rectangle((int)(-srcRect.Width/2),
(int)-srcRect.Height/2,
srcRect.Width,
srcRect.Height); // the center of apparent image is on origin
Matrix mx = new Matrix(); // create an identity matrix
mx.Scale(zoom, zoom); // zoom image
// Move image to view window center
mx.Translate(viewRectWidth / 2.0f, viewRectHeight / 2.0f, MatrixOrder.Append);
// Display image on widget
Graphics g = e.Graphics;
g.InterpolationMode = interMode;
g.Transform = mx;
g.DrawImage(image, destRect, srcRect, GraphicsUnit.Pixel);
}
}
My question is how to get the pixel value when I am on the MouseMove override method of this WinForm ?
I think understand that it is possible only in method with PaintEventArgs but I am not sure how to deal with it. I tried a lot of things but for now the better I got is use the mouse position on the screen and find the pixel value in the original bitmap with these "wrong" coordinates. I can't link this relative position on the screen with the real coordinates of the pixel of the image display at this place. Maybe there is method to "just" get the pixel value not passing through the image bitmap I use for the paint method ? Or maybe not.
Thank you in advance for your help. Best regards.
I couldn't completely understand your drawing code but you can do an inverse transformation on mouse coordinate. So, you can translate the mouse coordinate back to the origin and scale it by 1/zoom. This simple process gives you the image space coordinate.
I provide an example code with its own drawing code (not your code/algorithm) but that can still give you the idea of inverse transformation. It is pretty simple so look at the example code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace GetPixelFromZoomedImage
{
public partial class MainForm : Form
{
public Form1()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.ResizeRedraw, true);
InitializeComponent();
}
private float m_zoom = 1.0f;
private Bitmap m_image;
private Point m_origin = Point.Empty;
private Point m_delta = Point.Empty;
private SolidBrush m_brush = new SolidBrush(Color.Transparent);
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.TranslateTransform(m_origin.X, m_origin.Y);
g.ScaleTransform(m_zoom, m_zoom);
g.DrawImageUnscaled(m_image, Point.Empty);
g.ResetTransform();
g.FillRectangle(m_brush, ClientSize.Width - 50, 0, 50, 50);
base.OnPaint(e);
}
protected override void OnHandleCreated(EventArgs e)
{
m_image = (Bitmap)Image.FromFile("test.png");
base.OnHandleCreated(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if(e.Button == MouseButtons.Left)
{
m_delta = new Point(m_origin.X - e.X, m_origin.Y - e.Y);
}
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if(e.Button == MouseButtons.Left)
{
m_origin = new Point(e.X + m_delta.X, e.Y + m_delta.Y);
Invalidate();
}
int x = (int)((e.X - m_origin.X) / m_zoom);
int y = (int)((e.Y - m_origin.Y) / m_zoom);
if (x < 0 || x >= m_image.Width || y < 0 || y >= m_image.Height)
return;
m_brush.Color = m_image.GetPixel(x, y);
Invalidate();
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
}
protected override void OnMouseWheel(MouseEventArgs e)
{
float scaleFactor = 1.6f * (float)Math.Abs(e.Delta) / 120;
if(e.Delta > 0)
m_zoom *= scaleFactor;
else
m_zoom /= scaleFactor;
m_zoom = m_zoom > 64.0f ? 64.0f : m_zoom;
m_zoom = m_zoom < 0.1f ? 0.1f : m_zoom;
Invalidate();
base.OnMouseWheel(e);
}
}
}

Get coordinates points between x and y

I have a jpeg image in a picturebox, on page load i am drawing the rectangle based on the x(150) and y(440) coordinates. now when i mousemove on the picturebox i need to identify the rectangle by their coordinates and highlight the image. for example see the below image ..
lets take the first rectangle, on mouse move any points inside the rectangle i need to perform some actions.. how to find the coordinates between these x and y for the rectangle?..
A rectangle has 4 Points (edges):
Left
Top
Right
Bottom
If your mouse coordinates (MouseEventArgs properties) are between them, the mouse pointer is in the rectangle.
Are the mouse coordinates greater than right or bottom or lower than left / top, your mouse is outside the rectangle.
Taking #samgak`s Comment:
if(
(point.x >= rectangle.min_x) && (point.x <= rectangle.max_x) &&
(point.y >= rectangle.min_y) && (point.y <= rectangle.max_y)) {
//do something
}
and replacing point with e is exactly what you want.
Maybe the following link will help to understand:
How to check if a point is inside a rectangle
Provided images are the same size and assuming it is stored in variable imageSize (of type System.Drawing.Size) then:
Size imageSize = new Size(...) // define the size here
...
int row = point.y / imageSize.height;
int col = point.x / imageSize.width;
var rect = new Rectangle(col * imageSize.Width, row * imageSize.Height, imageSize.Width, imageSize.Height);
You can then use rect to draw you frame around the image (you may want to inflate the rectangle by a couple of pixels)
Hi Samgak /Clijsters,
I have completed my functionality
// page level declaration
private Rectangle SelectedRect;
public List<Rectangle> listRec = new List<Rectangle>();
// on page load add all the rectangle in the rectanglelist.
private void Highlightimage_Load(object sender, EventArgs e)
{
for (int i = 0; i < table.Rows.Count; i++)
{
int x = Convert.ToInt32(table.Rows[i][0]);
int y = Convert.ToInt32(table.Rows[i][1]);
int width = Convert.ToInt32(table.Rows[i][2]);
int height = Convert.ToInt32(table.Rows[i][3]);
SelectedRect.Size = new Size(width, height);
SelectedRect.X = x;
SelectedRect.Y = y;
listRec.Add(SelectedRect);
}
}
// draw the rectangle
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{ Graphics g = e.Graphics;
foreach (Rectangle rec in listRec)
{
Pen p = new Pen(Color.Red);
g.DrawRectangle(p, rec);
}
}
private Rectangle MakeRectangle(int x0, int y0, int x1, int y1)
{
return new Rectangle(
Math.Min(x0, x1),
Math.Min(y0, y1),
Math.Abs(x0 - x1),
Math.Abs(y0 - y1));
}
//finally on mouse move checking the condition
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
foreach (Rectangle rec in listRec)
{
SelectedRect = MakeRectangle(rec.Left, rec.Top, rec.Right, rec.Bottom);
if (
(e.X >= SelectedRect.Left) && (e.X <= SelectedRect.Right) &&
(e.Y >= SelectedRect.Top) && (e.Y <= SelectedRect.Bottom))
{
MessageBox.Show("test");
}
}
}
Also refered this link
Drawing Multiple Rectangles c#
I thought this will help some one.
Thanks
Dev

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

How do I clear a previously-drawn rectangle from an image using mouse/paint events?

I'm designing an app that loads an image into a PictureBox and allows the user to draw a selection rectangle on it. Currently, I use the Paint event and a boolean to clear the previously-drawn rectangle (since it's a drag-able selection box).
Question:
The code fails because the previous rectangle isn't cleared from the image. Although each rectangle that is drawn is transparent, the effect is an opaque rectangle because the previous rectangles aren't cleared. How can I clear these rectangles?
Logic:
saveState defaults to true. When the Paint event is triggered the first time, the state containing the normal image is saved. When a MouseDown event is triggered, we register the starting position of the rectangle and a boolean that indicates a rectangle is being drawn.
When a MouseMove event is triggered, we draw a rectangle at the current coordinates. Since the Paint event is triggered (I think) when this is drawn and saveState is false, we restore the normal image before drawing the rectangle.
Finally, when a MouseUp event is triggered, saveState is set to true, so the graphics state, with the last rectangle drawn, is saved, and we're back at the beginning.
I read about ControlPaint.DrawReversibleFrame, but since this article and this question give me the impression that it isn't designed for drawing on images, but rather on the screen or the form directly, I'm not sure that's what I need.
Code:
public partial class MainWindow : Form
{
private bool drawingRectangle;
private int x1, y1, x2, y2;
private Image currentImage;
private GraphicsState previousState;
private bool saveState;
public MainWindow()
{
InitializeComponent();
this.drawingRectangle = false;
this.saveState = true;
}
private void EditorPictureBox_MouseDown(object sender, MouseEventArgs e)
{
this.x1 = e.X;
this.y1 = e.Y;
this.drawingRectangle = true;
}
private void EditorPictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (this.drawingRectangle)
{
this.x2 = e.X;
this.y2 = e.Y;
Graphics g = Graphics.FromImage(this.currentImage);
int[] dim = ImageLibrary.CalculateRectangleDimensions(this.x1, this.y1, this.x2, this.y2);
g.FillRectangle(new SolidBrush(Color.FromArgb(100, 128, 255, 255)), dim[0], dim[1], dim[2], dim[3]);
this.Refresh();
}
}
private void EditorPictureBox_Paint(object sender, PaintEventArgs e)
{
if (this.saveState)
{
this.previousState = e.Graphics.Save();
this.saveState = false;
}
else
e.Graphics.Restore(this.previousState);
}
private void EditorPictureBox_MouseUp(object sender, MouseEventArgs e)
{
if (this.drawingRectangle)
{
this.drawingRectangle = false;
// When the mouse click is released, save the graphics state
this.saveState = true;
}
}
private void LoadImage2Button_Click(object sender, EventArgs e)
{
this.currentImage = Image.FromFile("goat2.jpg");
this.EditorPictureBox.Image = this.currentImage;
}
}
This is the code for CalculateRectangleDimensions (stored in a static library):
public static int[] CalculateRectangleDimensions(int x1, int y1, int x2, int y2)
{
int[] dimensions = new int[4]; // x1, y1, width, height
if (x1 <= x2) // Mouse was dragged to the right
{
dimensions[0] = x1;
dimensions[2] = x2 - x1;
}
else // Mouse was dragged to the right
{
dimensions[0] = x2;
dimensions[2] = x1 - x2;
}
if (y1 <= y2) // Mouse was dragged up
{
dimensions[1] = y1;
dimensions[3] = y2 - y1;
}
else // Mouse was dragged down
{
dimensions[1] = y2;
dimensions[3] = y1 - y2;
}
return dimensions;
}
Graphics.Save doesn't save the entire contents of the graphics when you call it, it just saves state information, like translations, scale, transforms, etc.
if you want to undo a draw you've already done, you'd have to do something like reversible paint does, or you'll have to redraw the original image when you want to undo your draw.

Keeping a PictureBox centered inside a container

I am designing a simple picture viewer with ability to do some basic image processing. At the moment I have the problem of keeping the PictureBox centered inside a TabPage all the time as well as keeping the picturebox width and size same as the picture its showing. So far I had no success.
I have the following code that I call in form constructor to position it in center. it works the first time to center the picturebox:
private void SetPictureBoxOriginalSizeAndLocation(bool makeImageNull = false, DockStyle dockStyle = DockStyle.None)
{
if (makeImageNull) picBoxView.Image = null;
picBoxView.Dock = dockStyle;
var xPoint = tabImageView.Location.X + ((splitContainer.Panel2.Width / 2) / 2);
var yPoint = tabImageView.Location.Y;
var width = tabImageView.Width / 2;
var height = (tabImageView.Height / 2) - toolStripImageView.Height;
if (picBoxView.Image == null) return;
//Resize image according to width
picBoxView.Image = ImageMethods.ResizeImage(picBoxView.Image.Tag.ToString(), width, height, false);
picBoxView.Location = new Point(xPoint, yPoint);
picBoxView.Width = width;
picBoxView.Height = height;
}
But it does not resize the picturebox to its image (you can see the black part that is back color for the picturebox control):
The problem is getting worse as soon as I resize the form, the picturebox position will goes to top:
I call the code above in form's resize event as well, no idea why it works when application starts. Would be nice if someone can tell me what properties I should take care of to achieve a nicely centered picturebox which always is as big as its image.
It's pretty easy if you just set the Anchor style to none:
picBoxView = new PictureBox();
picBoxView.SizeMode = PictureBoxSizeMode.AutoSize;
picBoxView.Anchor = AnchorStyles.None;
tabImageView.Controls.Add(picBoxView);
CenterPictureBox(picBoxView, myImage);
Then just center the PictureBox initially whenever you change the image of the PictureBox:
private void CenterPictureBox(PictureBox picBox, Bitmap picImage) {
picBox.Image = picImage;
picBox.Location = new Point((picBox.Parent.ClientSize.Width / 2) - (picImage.Width / 2),
(picBox.Parent.ClientSize.Height / 2) - (picImage.Height / 2));
picBox.Refresh();
}
Having the Anchor = None will center the PictureBox control for you whenever the parent container gets resized because it "isn't" anchored to the default Left and Top locations.
Givien a Form with TabControl, which has Dock set to Fill, below will keep your PictureBox in the centre. It also sets PictureBox size to Bitmap size:
public partial class Form1 : Form
{
Bitmap b = new Bitmap(320, 200);
public Form1()
{
InitializeComponent();
CenterTheBox();
}
private void Form1_Resize(object sender, EventArgs e)
{
CenterTheBox();
}
void CenterTheBox()
{
pictureBox1.Size = b.Size;
var left = (tabPage1.ClientRectangle.Width - pictureBox1.ClientRectangle.Width) / 2;
var top = (tabPage1.ClientRectangle.Height - pictureBox1.ClientRectangle.Height) / 2;
pictureBox1.Location = new Point(tabPage1.ClientRectangle.Location.X + left, tabPage1.ClientRectangle.Location.Y + top);
}
}
I believe your problem lies here
var xPoint = tabImageView.Location.X + ((splitContainer.Panel2.Width / 2) / 2);
var yPoint = tabImageView.Location.Y;
var width = tabImageView.Width / 2;
var height = (tabImageView.Height / 2) - toolStripImageView.Height;
ypoint is alwways set to tabImageView Y, althought it should be set to
tabImageView.Location.Y + (tabImageView.Size.Height - picBoxView.Size.Height)/2
should be almost the same with xPoint
tabImageView.Location.X + (tabImageView.Size.Width - picBoxView.Size.Width)/2
and
width = picBoxView.Image.Width;
height = picBoxView.Image.Height;

Categories