Use TrackBar to Zoom In/out of Picturebox Proportionately - c#

I'm using WinForms. In my form i have a picturebox that i want to zoom in and out using the track bar. My picturebox is set to zoom-mode. I want the image and picturebox to be proportion height/width when i drag the bar. How can i accomplish this?
private void Open_btn_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
Image bmp;
bmp = new Bitmap(openFileDialog1.FileName);
if (bmp == null)
{
MessageBox.Show("Loading image failed", "Error", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
}
else
{
pictureBox1.Image = bmp;
openFileDialog1.Dispose();
}
}
}
private void zoomSlider_Scroll(object sender, EventArgs e)
{
if(TrackBar1.Value == 1)
{
pictureBox1.Height += 50;
pictureBox1.Width += 50;
}
if(TrackBar1.Value == 2)
{
pictureBox1.Height += 100;
pictureBox1.Width += 100;
}
if(TrackBar1.Value == 3)
{
pictureBox1.Height += 200;
pictureBox1.Width += 200;
}
//This is not exactly what i had in mind...
}

When the form is originally created you have to save the size
Form1 : Form
{
private Size _pictOriginalSize;
Form1()
{
InitialiseComponent();
_pictOriginalSize = pictureBox1.Size;
zoomSlider.Minimum = 0;
zoomSldier.Maximum = 1000;
...
}
Now you know what it's unzoomed size was.
Next you will need to you will need to convert the value of the slider into into a scale factor.
private void zoomSlider_Scroll(object sender, EventArgs e)
{
const double MaxScale = 5.0; // The scale factor when the is at it's max
double scale = Math.Pow(MaxScale, TrackBar1.Value / TrackBar1.Maximum);
Size newSize = new Size((int) (_pictOrignalSize.Width * scale),
(int) (_pictOrignalSize.Height * scale));
pictureBox.Size = newSize;
}
I am using the Math.Pow function to convert the scale from 1 to 5 on an exponential scale - you may want to use a different technique you could consider
having the slider have values 1 to 5 and the Value simply becomes the scale: scale = zoomSlider.Value - but this only gives you a resoultion of 1.
having the slider have values 1 to 5000 and do a double division double scale = zoomSlider.Value / 1000.0; This gives you a 1000 different resolutions.
NB: I am using 5 as an example only - you can use any value for the max scale factor.

Related

zoom an image in a second picturebox following cursor

i have a picturebox that contain an image (1280 X 720), i want to create a second picturebox that contain a zoomed version of the image centered around the cursor (for example a 40 X 40 square around the cursor zomed to be in a 120 X 120 square picturebox) that follow the cursor in real time in real time (if it's possible also to have a cross in the middle of the picturebox that show the precise placing of the cursor is even better).
private void Button1_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "All jpg files (*.jpg)|*.jpg";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
Bitmap img = new Bitmap(openFileDialog1.FileName);
double imageHeight = img.Height;
double imageWidth = img.Width;
pictureBox1.Image = img;
}
}
private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
{
int xupleft = e.X - 20;
int yupleft = e.Y - 20;
Rectangle myrectangle = new Rectangle(xupleft, yupleft, 40, 40);
pictureBox2.Image = pictureBox1.Image;
}
Here is a simple example with the layout I described in the comment:
Both PictureBoxes are nested in Panels.
The first one is in Zoom mode and upon loading a file its size is adapted to avoid blank stripes to the sides. Its parent panel is used to reset to the maximum allowed size.
The 2nd one is in AutoSize mode and its parent panel is used to 1) hide the outer portions and 2) to calculate the offset to center the image.
Here is the simple Paint event for pbox1:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Size sz = pictureBox1.ClientSize;
Point pt = pictureBox1.PointToClient(Control.MousePosition);
e.Graphics.DrawLine(Pens.OrangeRed, pt.X, 0, pt.X, sz.Height);
e.Graphics.DrawLine(Pens.OrangeRed, 0, pt.Y, sz.Width, pt.Y);
}
Here is the MouseMove the triggers the Paint and moves pbox2:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
pictureBox1.Invalidate();
float f = 1f * pictureBox2.ClientSize.Width / pictureBox1.ClientSize.Width;
Size s2 = pictureBox2.Parent.ClientSize;
Point p2 = Point.Round(new PointF(s2.Width/2 - e.X * f , s2.Height/2 - e.Y * f ));
pictureBox2.Location = p2;
}
The file loading is a bit tricky, as it needs to analyze the aspect ratios of image and pbox:
void loadFile(string fileName)
{
if (File.Exists(fileName))
{
pictureBox1.Size = panel1.ClientSize;
pictureBox1.Location = Point.Empty;
pictureBox1.Image = Image.FromFile(fileName);
pictureBox2.Image = Image.FromFile(fileName);
pictureBox2.Location = Point.Empty;
}
Size csz = pictureBox1.ClientSize;
Size isz = pictureBox1.Image.Size;
float iar = 1f * isz.Width / isz.Height; // aspect..
float car = 1f * csz.Width / csz.Height; //..ratios
if (iar < car)
{
pictureBox1.ClientSize = new Size((int)(pictureBox1.ClientSize.Height * iar),
pictureBox1.ClientSize.Height);
}
else if (iar > car)
{
pictureBox1.ClientSize = new Size((pictureBox1.ClientSize.Width,
(int)(pictureBox1.ClientSize.Width / iar));
}
}
Note that before loading the new images one should Dispose of the previous images! Also that after setting the pbox2.SizeMode to Autosize one could set it to Zoom and scale its Size up or down to zoom in or out, if one keeps the aspect ratio the same.
Result:

free-form crop selection doesn't work properly

I made a program that lets you do a random selection then the selected area to save into a new image, but I got a problem it doesn't work how it's supposed to... I will post my code here so you can have a look:
private List<Point> Points = null;
private bool Selecting = false;
private Bitmap SelectedArea = null;
private void pictureBox5_MouseDown(object sender, MouseEventArgs e)
{
Points = new List<Point>();
Selecting = true;
}
private void pictureBox5_MouseMove(object sender, MouseEventArgs e)
{
if (!Selecting) return;
Points.Add(new Point(e.X, e.Y));
pictureBox5.Invalidate();
}
private void pictureBox5_MouseUp(object sender, MouseEventArgs e)
{
Selecting = false;
// Copy the selected area.
SelectedArea = GetSelectedArea(pictureBox5.Image, Color.Transparent, Points);
SelectedArea.Save(#"C:\Users\User\Desktop\Gallery\image" + NumberOfClick.ToString() + "cropped.jpeg", ImageFormat.Jpeg);
string filename = #"C:\Users\User\Desktop\Gallery\image" + NumberOfClick.ToString() + "cropped.jpeg";
if(File.Exists(filename))
{
button1.Visible = true;
pictureBox5.Visible = false;
}
}
private void pictureBox5_Paint(object sender, PaintEventArgs e)
{
if ((Points != null) && (Points.Count > 1))
{
using (Pen dashed_pen = new Pen(Color.Black))
{
dashed_pen.DashPattern = new float[] { 5, 5 };
e.Graphics.DrawLines(Pens.White, Points.ToArray());
e.Graphics.DrawLines(dashed_pen, Points.ToArray());
}
}
}
private Bitmap GetSelectedArea(Image source, Color bg_color, List<Point> points)
{
// Make a new bitmap that has the background
// color except in the selected area.
Bitmap big_bm = new Bitmap(source);
using (Graphics gr = Graphics.FromImage(big_bm))
{
// Set the background color.
gr.Clear(bg_color);
// Make a brush out of the original image.
using (Brush br = new TextureBrush(source))
{
// Fill the selected area with the brush.
gr.FillPolygon(br, points.ToArray());
// Find the bounds of the selected area.
Rectangle source_rect = GetPointListBounds(points);
// Make a bitmap that only holds the selected area.
Bitmap result = new Bitmap(
source_rect.Width, source_rect.Height);
// Copy the selected area to the result bitmap.
using (Graphics result_gr = Graphics.FromImage(result))
{
Rectangle dest_rect = new Rectangle(0, 0,
source_rect.Width, source_rect.Height);
result_gr.DrawImage(big_bm, dest_rect,
source_rect, GraphicsUnit.Pixel);
}
// Return the result.
return result;
}
}
}
private Rectangle GetPointListBounds(List<Point> points)
{
int xmin = points[0].X;
int xmax = xmin;
int ymin = points[0].Y;
int ymax = ymin;
for (int i = 1; i < points.Count; i++)
{
if (xmin > points[i].X) xmin = points[i].X;
if (xmax < points[i].X) xmax = points[i].X;
if (ymin > points[i].Y) ymin = points[i].Y;
if (ymax < points[i].Y) ymax = points[i].Y;
}
return new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin);
}
This is how I am doing and saving the cropped images.
And also this is how I am uploading the pictures:
OpenFileDialog f = new OpenFileDialog();
f.Filter = "Image files (*.jpg, *.jpeg, *.jpe, *.jfif, *.png) | *.jpg; *.jpeg; *.jpe; *.jfif; *.png";
if (f.ShowDialog() == DialogResult.OK)
{
currentImage = Image.FromFile(f.FileName);
pictureBox1.Image = currentImage;
}
pictureBox1.Image.Save(#"C:\Users\User\Desktop\Gallery\image1.jpeg", ImageFormat.Jpeg);
DialogResult result = MessageBox.Show("Crop your image", "Information", MessageBoxButtons.OK);
if(result == DialogResult.OK)
{
pictureBox5.Visible = true;
button1.Visible = false;
pictureBox5.Image = pictureBox1.Image;
}
In pictureBox5 I am selecting and cropping the picture.
mySelection
croppedImage
You need to calculate the zoom and the offset of the image when it is zoomed.
Here is how to do that; this assumes the PictureBox is indeed in Zoom mode, not in Stretch mode. If you stretch it you need to calculate the zooms for x and y separately..
SizeF sp = pictureBox5.ClientSize;
SizeF si = pictureBox5.Image.Size;
float rp = sp.Width / sp.Height; // calculate the ratios of
float ri = si.Width / si.Height; // pbox and image
float zoom = (rp > ri) ? sp.Height / si.Height : sp.Width / si.Width;
float offx = (rp > ri) ? (sp.Width - si.Width * zoom) / 2 : 0;
float offy = (rp <= ri)? (sp.Height - si.Height * zoom) / 2 : 0;
Point offset = Point.Round(new PointF(offx, offy));
You calculate this after setting the Image and after resizing the PictureBox..
Now you can transform each drawn point into a zoomed or an unzoomed coordinate:
PointF zoomed(Point p1, float zoom, Point offset)
{
return (new PointF(p1.X * zoom + offset.X, p1.Y * zoom + offset.Y));
}
PointF unZoomed(Point p1, float zoom, Point offset)
{
return (new PointF((p1.X - offset.X) / zoom, (p1.Y - offset.Y) / zoom));
}
Here is a demo the draws on to either a normal (left) or a zoomed in (middle) image. To the right is the result of placing your GetSelectedArea bitmap onto a PictureBox with a checkerbox background:
Case 1: If you store the points as they come in: In your GetSelectedArea method use this point list instead:
private Bitmap GetSelectedArea(Image source, Color bg_color, List<Point> points)
{
var unzoomedPoints =
points.Select(x => Point.Round((unZoomed(Point.Round(x), zoom, offset))))
.ToList();
// Make a new bitmap that has the background
After this replace each reference to points in the method by one to unzoomedPoints. Actually there are just two of them..
Case 2: If you store the points already 'unZoomed' :
Points.Add(unZoomed(e.Location, zoom, offset));
you can use the list directly..

Zooming / Resizing Images get blurry

I’m using WinForms. My application works like a simple image viewer. It opens Image files goes to the next page, zooms in and zooms out. The problem with my application is that when I re-size or zoom in the image, the image starts getting blurry. How do I prevent my images from getting blurry when I’m zooming out or zooming in?
When i view and re-size the same image on Windows Photo Viewer the images is clear. When i open the image in my application the image is clear but as soon as i start re sizing it for example when i start using zoom it starts becoming blurry.
I tried implementing this code in my zoom out method but i didn't couldn't figure out how to do it.
Size newSize = new Size((int)(originalBitmap.Width * zoomFactor),
(int)(originalBitmap.Height * zoomFactor));
Bitmap bmp = new Bitmap(originalBitmap, newSize);
The image dimensions i look at:
Dimensions: 2540 x 3289
Dimensions: 2533 x 3284
Dimensions: 2538 x 3282
Dimensions: 2479 x 3508
FileStream _stream;
Image _myImg; // setting the selected tiff
string _fileName;
private int intCurrPage = 0; // defining the current page
private void Open_Btn_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
lblFile.Text = openFileDialog1.FileName; //Shows the filename in the lable
Open_Image_Control();
Image img = Image.FromFile(openFileDialog1.FileName);
pictureBox1.Image = img;
pictureBox1.Width = img.Width;
pictureBox1.Height = img.Height;
this.pictureBox1.Image.RotateFlip(RotateFlipType.Rotate90FlipNone); // Rotates image 90 degrees
}
}
public void Open_Image_Control()
{
Image myBmp;
if (_myImg == null)
{
_fileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
File.Copy(#lblFile.Text, _fileName);
_stream = new FileStream(_fileName, FileMode.Open, FileAccess.Read);
_myImg = Image.FromStream(_stream);
}
int intPages = _myImg.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page); // getting the number of pages of this tiff
intPages--; // the first page is 0 so we must correct the number of pages to -1
lblNumPages.Text = Convert.ToString(intPages); // showing the number of pages
lblCurrPage.Text = Convert.ToString(intCurrPage); // showing the number of page on which we're on
_myImg.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, intCurrPage); // going to the selected page
myBmp = new Bitmap(_myImg, pictureBox1.Width, pictureBox1.Height);
pictureBox1.Image = myBmp; // showing the page in the pictureBox1
}
private void ZoomIN_Btn_Click(object sender, EventArgs e)
{
this.pictureBox1.Width += 175; //Zoom width by 75
this.pictureBox1.Height += 175; //Zoom height by 75
}
private void ZoomOut_Btn_Click(object sender, EventArgs e)
{
this.pictureBox1.Width -= 175; //Zoom out width by 75
this.pictureBox1.Height -= 175; //Zoom out height by 75
}
private void NextPage_btn_Click(object sender, EventArgs e)
{
if (intCurrPage == Convert.ToInt32(lblNumPages.Text)) // if you have reached the last page it ends here
// the "-1" should be there for normalizing the number of pages
{ intCurrPage = Convert.ToInt32(lblNumPages.Text); }
else
{
intCurrPage++; //page increment (Goes to next page)
Open_Image_Control();
this.pictureBox1.Image.RotateFlip(RotateFlipType.Rotate90FlipNone); // Rotates image 90 degrees
}
}

Draw line in picturebox and redraw on zoom

I'm drawing a line in a picturebox this way:
horizontalstart = new Point(0, e.Y); //Start point of Horizontal line.
horizontalend = new Point(picbox_mpr.Width, e.Y); //End point of Horizontal line.
verticalstart = new Point(e.X, 0); //Start point of Vertical line
verticalend = new Point(e.X, picbox_mpr.Height); //End point of Vertical line.
Then on the paint event I do this:
e.Graphics.DrawLine(redline, horizontalstart, horizontalend); //Draw Horizontal line.
e.Graphics.DrawLine(redline, verticalstart, verticalend); //Draw Vertical line.
Pretty simple, now, my image can zoom and here's where I struggle..
How do I keep the line in the same spot that was drawn even if I zoom the image?
Instead of storing an absolute integer coordinate, store a decimal value representing the "percentage" of that coord with respect to the width/height of the image. So if the X value was 10 and the width was 100, you store 0.1. Let's say the image was zoomed and it was now 300 wide. The 0.1 would now translate to 0.1 * 300 = 30. You can store the "percentage" X,Y pairs in PointF() instead of Point().
Here's a quick example to play with:
public partial class Form1 : Form
{
private List<Tuple<PointF, PointF>> Points = new List<Tuple<PointF, PointF>>();
public Form1()
{
InitializeComponent();
this.Shown += new EventHandler(Form1_Shown);
this.pictureBox1.BackColor = Color.Red;
this.pictureBox1.SizeChanged += new EventHandler(pictureBox1_SizeChanged);
this.pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);
}
void Form1_Shown(object sender, EventArgs e)
{
// convert absolute points:
Point ptStart = new Point(100, 25);
Point ptEnd = new Point(300, 75);
// to percentages:
PointF ptFstart = new PointF((float)ptStart.X / (float)pictureBox1.Width, (float)ptStart.Y / (float)pictureBox1.Height);
PointF ptFend = new PointF((float)ptEnd.X / (float)pictureBox1.Width, (float)ptEnd.Y / (float)pictureBox1.Height);
// add the percentage point to our list:
Points.Add(new Tuple<PointF, PointF>(ptFstart, ptFend));
pictureBox1.Refresh();
}
private void button1_Click(object sender, EventArgs e)
{
// increase the size of the picturebox
// watch how the line(s) change with the changed picturebox
pictureBox1.Size = new Size(pictureBox1.Width + 50, pictureBox1.Height + 50);
}
void pictureBox1_SizeChanged(object sender, EventArgs e)
{
pictureBox1.Refresh();
}
void pictureBox1_Paint(object sender, PaintEventArgs e)
{
foreach (Tuple<PointF, PointF> tup in Points)
{
// convert the percentages back to absolute coord based on the current size:
Point ptStart = new Point((int)(tup.Item1.X * pictureBox1.Width), (int)(tup.Item1.Y * pictureBox1.Height));
Point ptEnd = new Point((int)(tup.Item2.X * pictureBox1.Width), (int)(tup.Item2.Y * pictureBox1.Height));
e.Graphics.DrawLine(Pens.Black, ptStart, ptEnd);
}
}
}

Problem with the scrolling!

I have implemented a picturebox in my in my form.I even implemented the scrollbars to make the image fit in it.so now the problem is when i try to scroll the button down,it scrolls down and immediately when i leave the mouse the button scrolls up ..here is the code implemnted please give some suggestions..
public void DisplayScrollBars()
{
// If the image is wider than the PictureBox, show the HScrollBar.
if (pictureBox1.Width > pictureBox1.Image.Width - this.vScrollBar1.Width)
{
hScrollBar1.Visible = false;
}
else
{
hScrollBar1.Visible = true;
}
// If the image is taller than the PictureBox, show the VScrollBar.
if (pictureBox1.Height >
pictureBox1.Image.Height - this.hScrollBar1.Height)
{
vScrollBar1.Visible = false;
}
else
{
vScrollBar1.Visible = true;
}
}
private void HandleScroll(Object sender, ScrollEventArgs se)
{
/* Create a graphics object and draw a portion
of the image in the PictureBox. */
Graphics g = pictureBox1.CreateGraphics();
g.DrawImage(pictureBox1.Image,
new Rectangle(0, 0, pictureBox1.Right - vScrollBar1.Width,
pictureBox1.Bottom - hScrollBar1.Height),
new Rectangle(hScrollBar1.Value, vScrollBar1.Value,
pictureBox1.Right - vScrollBar1.Width,
pictureBox1.Bottom - hScrollBar1.Height),
GraphicsUnit.Pixel);
pictureBox1.Update();
}
public void SetScrollBarValues()
{
// Set the Maximum, Minimum, LargeChange and SmallChange properties.
this.vScrollBar1.Minimum = 0;
this.hScrollBar1.Minimum = 0;
// If the offset does not make the Maximum less than zero, set its value.
if ((this.pictureBox1.Image.Size.Width - pictureBox1.ClientSize.Width) > 0)
{
this.hScrollBar1.Maximum =
this.pictureBox1.Image.Size.Width - pictureBox1.ClientSize.Width;
}
// If the VScrollBar is visible, adjust the Maximum of the
// HSCrollBar to account for the width of the VScrollBar.
if (this.vScrollBar1.Visible)
{
this.hScrollBar1.Maximum += this.vScrollBar1.Width;
}
this.hScrollBar1.LargeChange = this.hScrollBar1.Maximum / 10;
this.hScrollBar1.SmallChange = this.hScrollBar1.Maximum / 20;
// Adjust the Maximum value to make the raw Maximum value
// attainable by user interaction.
this.hScrollBar1.Maximum += this.hScrollBar1.LargeChange;
// If the offset does not make the Maximum less than zero, set its value.
if ((this.pictureBox1.Image.Size.Height - pictureBox1.ClientSize.Height) > 0)
{
this.vScrollBar1.Maximum =
this.pictureBox1.Image.Size.Height - pictureBox1.ClientSize.Height;
}
// If the HScrollBar is visible, adjust the Maximum of the
// VSCrollBar to account for the width of the HScrollBar.
if (this.hScrollBar1.Visible)
{
this.vScrollBar1.Maximum += this.hScrollBar1.Height;
}
this.vScrollBar1.LargeChange = this.vScrollBar1.Maximum / 10;
this.vScrollBar1.SmallChange = this.vScrollBar1.Maximum / 20;
// Adjust the Maximum value to make the raw Maximum value
// attainable by user interaction.
this.vScrollBar1.Maximum += this.vScrollBar1.LargeChange;
}
private void pictureBox1_Resize(object sender, EventArgs e)
{
// If the PictureBox has an image, see if it needs
// scrollbars and refresh the image.
if (pictureBox1.Image != null)
{
this.DisplayScrollBars();
this.SetScrollBarValues();
this.Refresh();
}
}
As I comment above, the correct way of doing this is to put your pictureBox in a panel and set the panel.AutoScroll=true. Also you need to set pictureBox.SizeMode=AutoSize so it is sized equal to the size of the image that it contains. Check: PictureBoxSizeMode Enumeration

Categories

Resources