I'm trying to achieve an effect similar to this site. I am basically trying to implement a way to "compare" two similar images (with different colors, etc). I managed to do this in a not so brilliant way using two PictureBox controls (Winforms) one next to the other, and changing their Size and Location attributes on a MouseMove event.
The result works, but it flickers a lot and it's not really the best way to do it.
Is there a better way to do this, maybe with a WPF or by changing the code in any way? Here it is:
private void pbImg1_MouseMove(object sender, MouseEventArgs e)
{
pbImg2.Image = CropImage(array[1], new Rectangle(pbImg1.Size.Width, 0, totalSize.Width - pbImg1.Size.Width, 240));
pbImg1.Size = new Size(e.X, pbImg1.Height);
pbImg2.Location = new Point(pbImg1.Size.Width + pbImg1.Location.X, pbImg2.Location.Y);
pbImg2.Size = new Size(totalSize.Width - pbImg1.Size.Width, 240);
lpbImg1Size.Text = pbImg1.Size.ToString();
lpbImg2Size.Text = pbImg2.Size.ToString();
lpbImg1Location.Text = pbImg1.Location.ToString();
lpbImg2Location.Text = pbImg2.Location.ToString();
}
private void pbImg2_MouseMove(object sender, MouseEventArgs e)
{
pbImg1.Image = CropImage(array[0], new Rectangle(0, 0, totalSize.Width - pbImg2.Size.Width, 240));
pbImg1.Size = new Size(pbImg1.Width + e.X, 240);
lpbImg1Size.Text = pbImg1.Size.ToString();
lpbImg2Size.Text = pbImg2.Size.ToString();
lpbImg1Location.Text = pbImg1.Location.ToString();
lpbImg2Location.Text = pbImg2.Location.ToString();
}
public Bitmap CropImage(Bitmap source, Rectangle section)
{
// An empty bitmap which will hold the cropped image
//TRY CATCH
Bitmap bmp = new Bitmap(section.Width, section.Height);
Graphics g = Graphics.FromImage(bmp);
// Draw the given area (section) of the source image
// at location 0,0 on the empty bitmap (bmp)
g.DrawImage(source, 0, 0, section, GraphicsUnit.Pixel);
return bmp;
}
And here you can see the behavior of the program:
https://gfycat.com/VillainousReadyAmazonparrot
You need two images imageLeft, imageRight with the same size as the picturebox:
private int pos = 0; //x coordinate of mouse in picturebox
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if(pos == 0)
{
e.Graphics.DrawImage(Properties.Resources.imageRight, new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height));
}
else
{
e.Graphics.DrawImage(Properties.Resources.imageLeft, new Rectangle(0, 0, pos, pictureBox1.Height),
new Rectangle(0, 0, pos, pictureBox1.Height), GraphicsUnit.Pixel);
e.Graphics.DrawImage(Properties.Resources.imageRight, new Rectangle(pos, 0, pictureBox1.Width - pos, pictureBox1.Height),
new Rectangle(pos, 0, pictureBox1.Width - pos, pictureBox1.Height), GraphicsUnit.Pixel);
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
pos = e.X;
pictureBox1.Invalidate();
}
Related
How to change the size of the image and the size of the rectangle that was drawn inside the image so that the rectangle does not lose the position of the x and the y.
I did the following code, but it doesn't keep the position of the rectangle
int index3 = dataGridView1.CurrentCell.RowIndex;
xxx = Convert.ToInt32(dataGridView1.Rows[index3].Cells[10].Value.ToString());
yyy = Convert.ToInt32(dataGridView1.Rows[index3].Cells[11].Value.ToString());
hhh = Convert.ToInt32(dataGridView1.Rows[index3].Cells[12].Value.ToString());
www = Convert.ToInt32(dataGridView1.Rows[index3].Cells[13].Value.ToString());
Rectangle r = new Rectangle(xxx, yyy, www, hhh);
int newX = (int)(xxx+ (xxx*0.2));
int newY = (int)(yyy +( yyy*0.2));
int newWidth = (int)(r.Width +(r.Width*0.2));
int newHeight = (int)(r.Height +(r.Height*0.2));
picturModule.Size = new Size((int)(picturModule.Width+(picturModule.Width*0.2)),(int)(picturModule.Height+(picturModule.Height*0.2)));
r = new Rectangle(newX, newY, newWidth, newHeight);
Pen pen = new Pen(Color.GreenYellow, 4);
picturModule.CreateGraphics().DrawRectangle(pen, r);
The problem is when changing the size of the image after drawing the
rectangle on the image, the rectangle is not moved to the correct X
and Y point and its position changes. I want when changing the size of
the image that the size of the rectangle changes and keeps the same
point from which you started
Okay. What you can do is store the position and size of the Rectangle as a "percentage" of the width/height of the image/picturebox. This could be done using RectangleF and floating point values.
Now you can use those "percentage" values and multiply them by the new picturebox width/height to get the scaled location and size.
Here's an example:
Code that generated the animation:
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private bool drawRect = false;
private RectangleF rcF;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (drawRect)
{
Point pt = new Point((int)(rcF.X * pictureBox1.Width),
(int)(rcF.Y * pictureBox1.Height));
Size sz = new Size((int)(rcF.Width * pictureBox1.Width),
(int)(rcF.Height * pictureBox1.Height));
e.Graphics.DrawRectangle(Pens.Black, new Rectangle(pt, sz));
}
}
private void button1_Click(object sender, EventArgs e)
{
Rectangle initialRectangle = new Rectangle(new Point(300, 150), new Size(125, 50));
PointF ptF = new PointF((float)initialRectangle.X / pictureBox1.Width,
(float)initialRectangle.Y / pictureBox1.Height);
SizeF szF = new SizeF((float)initialRectangle.Width / pictureBox1.Width,
(float)initialRectangle.Height / pictureBox1.Height);
rcF = new RectangleF(ptF, szF);
drawRect = true;
pictureBox1.Invalidate();
}
private void pictureBox1_SizeChanged(object sender, EventArgs e)
{
pictureBox1.Invalidate();
}
}
First i'm drawing a rectangle on the pictureBox1 with the mouse
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
rect = new Rectangle(e.X, e.Y, 0, 0);
painting = true;
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
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.pictureBox1.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (painting == true)
{
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, rect);
}
}
}
The variable rect is global Rectangle and painting is global bool.
Then I did inside the pictureBox1 mouseup event
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
pictureBox1.Image = SaveRectanglePart(pictureBox1.Image, rect);
}
And the method SaveRectanglePart
Bitmap bmptoreturn;
public Bitmap SaveRectanglePart(Image image, RectangleF sourceRect)
{
using (var bmp = new Bitmap((int)sourceRect.Width, (int)sourceRect.Height))
{
using (var graphics = Graphics.FromImage(bmp))
{
graphics.DrawImage(image, 0.0f, 0.0f, sourceRect, GraphicsUnit.Pixel);
}
bmptoreturn = bmp;
}
return bmptoreturn;
}
What i want to do is when i finish drawing the rectangle in the mouseup event to clear the pictureBox1 and replace the image in there with the rectangle image only.
But i'm getting exception parameter not valid in the mouseup event
pictureBox1.Image = SaveBitmapPart(pictureBox1.Image, rect);
And should i dispose somewhere the variable bmptoreturn ?
In the function SaveRectanglePart the variable bmp is Dispose of before the function returns as a result of the using statement. You need to remove the using statement and the code should work.
Bitmap bmptoreturn;
public Bitmap SaveRectanglePart(Image image, RectangleF sourceRect)
{
var bmp = new Bitmap((int)sourceRect.Width, (int)sourceRect.Height)
using (var graphics = Graphics.FromImage(bmp))
{
graphics.DrawImage(image, 0.0f, 0.0f, sourceRect, GraphicsUnit.Pixel);
}
bmptoreturn = bmp;
return bmptoreturn;
}
But we have the issue of what bmptoreturn and pictureBox1.Image were referencing before they were set. The old Image/Bitmap the reference will be lost in memory until garbage collection comes along to free their memory. To be a good programmer we need to Dispose of these Image/Bitmap when we are done with them.
Image tmp = bmptoreturn;
bmptoreturn = bmp;
if(tmp != null)
tmp.Dispose();
...
Image tmp = pictureBox1.Image;
pictureBox1.Image = SaveBitmapPart(pictureBox1.Image, rect);
if(tmp != null)
tmp.Dispose();
Also, I am not sure why you are using bmptoreturn but it is not needed in the code from what I can tell. You can simply return bmp if bmptoreturn is not being used elsewhere.
I have Image of width 1171 and height 1181. I need that image into print page using printDocument...So Final image size would be width 596 and height 892. So it covers all area of page without any space.
My problem is I am able to resize it but it does not cover the whole space of page...And with different solution it covers the whole page area but image cutted down.
So what I have to do for solving this issue?
My code as per below...
private void btnPrint_Click(object sender, EventArgs e)
{
btnPrint.Enabled = false;
try
{
printDocument1.Print();
this.Close();
}
catch (Exception err)
{
MessageBox.Show("Printing failed :" + err.Message);
btnPrint.Enabled = true;
}
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
Image img = ((PictureBox)pictureBox1).Image;
Bitmap bmp = (Bitmap)img;
bmp.Save("abcc");
Bitmap bitmap = new Bitmap(bmp, new Size(892, 596));
// Bitmap b = ResizeBitmap(bmp, 892, 596);
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
e.Graphics.CompositingMode = CompositingMode.SourceCopy;
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
e.Graphics.Transform.Scale(0, 0);
e.Graphics.DrawImage(bitmap,new Rectangle(0,0,892,596),new Rectangle(0,0,bmp.Width,bmp.Height),GraphicsUnit.Pixel);
}
I'm working on a winforms project that involves cropping an image. My goal is to do this by using a fixed-size draggable picturebox control, allowing the user to select the area they want to preserve.
My problem is when I crop the image; it "works", but the crop area offsets a little. Here's the result I get:
To clarify, I'm not talking about the zooming, that's per design. Notice the orange box is mostly focusing on the eye of the storm, but the cropped image is not.
This is my code for the crop operation:
private void tsbRecortar_Click(object sender, EventArgs e)
{
Rectangle recorte = new Rectangle(pbxSeleccion.Location.X, pbxSeleccion.Location.Y, pbxSeleccion.Width, pbxSeleccion.Height);
foto = recortarImagen(foto, recorte);
pbxImagen.Image = foto;
}
private Image recortarImagen(Image imagen, Rectangle recuadro)
{
try
{
Bitmap bitmap = new Bitmap(imagen);
Bitmap cropedBitmap = bitmap.Clone(recuadro, bitmap.PixelFormat);
return (Image)(cropedBitmap);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error");
return null;
}
}
pbxSeleccion is the draggable orange rectangle; its parent is pbxImage (I re-parent it on form's load).
As you can see, I'm using the coordinates of pbxSeleccion to define the starting point of the crop area, but is not working as expected... sometimes, I even get an "Out of Memory" exception.
I think this has to do with how the image loads in the parent picturebox, something about how the margin is handled "under the hood", but nothing I tried fixes it... just changes the magnitude of the offset.
Searching the web and SO has helped me a lot, but for this particular issue, I can't seem to find an answer... please, feel free to point out improvements to my code, I haven't been coding for long and I'm new to C# and .NET
Any help is highly appreciated. Cheers!
Suppose your original image is displayed in a PictureBox. You passed in the wrong location of the orange cropping window. Here is the corrected code for you:
private void tsbRecortar_Click(object sender, EventArgs e){
Point p = yourPictureBox.PointToClient(pbxSelection.PointToScreen(Point.Empty));
Rectangle recorte = new Rectangle(p.X, p.Y, pbxSeleccion.Width, pbxSeleccion.Height);
foto = recortarImagen(foto, recorte);
pbxImagen.Image = foto;
}
I use PointToClient and PointToScreen here because I think it's the best way to do. You can then change the container of your pictureBox safely without having to modify the code. If you use the code like the following, it's not dynamically enough when you want to place your pictureBox in another container:
Rectangle recorte = new Rectangle(pbxSeleccion.X + yourPictureBox.Left,
pbxSeleccion.Y + yourPictureBox.Top,
pbxSeleccion.Width, pbxSeleccion.Height);
NOTE: you can also use RectangleToClient and RectangleToScreen like this:
private void tsbRecortar_Click(object sender, EventArgs e){
Rectangle recorte = yourPictureBox.RectangleToClient(pbxSeleccion.RectangleToScreen(pbxSeleccion.ClientRectangle));
foto = recortarImagen(foto, recorte);
pbxImagen.Image = foto;
}
Try This Code for Cropping the Image in picturebox
public static Image Fit2PictureBox(this Image image, PictureBox picBox)
{
Bitmap bmp = null;
Graphics g;
// Scale:
double scaleY = (double)image.Width / picBox.Width;
double scaleX = (double)image.Height / picBox.Height;
double scale = scaleY < scaleX ? scaleX : scaleY;
// Create new bitmap:
bmp = new Bitmap(
(int)((double)image.Width / scale),
(int)((double)image.Height / scale));
// Set resolution of the new image:
bmp.SetResolution(
image.HorizontalResolution,
image.VerticalResolution);
// Create graphics:
g = Graphics.FromImage(bmp);
// Set interpolation mode:
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Draw the new image:
g.DrawImage(
image,
new Rectangle( // Ziel
0, 0,
bmp.Width, bmp.Height),
new Rectangle( // Quelle
0, 0,
image.Width, image.Height),
GraphicsUnit.Pixel);
// Release the resources of the graphics:
g.Dispose();
// Release the resources of the origin image:
image.Dispose();
return bmp;
}
public static Image Crop(this Image image, Rectangle selection)
{
Bitmap bmp = image as Bitmap;
// Check if it is a bitmap:
if (bmp == null)
throw new ArgumentException("Kein gültiges Bild (Bitmap)");
// Crop the image:
Bitmap cropBmp = bmp.Clone(selection, bmp.PixelFormat);
// Release the resources:
image.Dispose();
return cropBmp;
}
Write The Following Code For Mouse Event on PictureBox
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_selecting = true;
_selection = new Rectangle(new Point(e.X, e.Y), new Size());
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && _selecting)
{
// Create cropped image:
try
{
Image img = pictureBox1.Image.Crop(_selection);
// Fit image to the picturebox:
pictureBox1.Image = img.Fit2PictureBox(pictureBox1);
}
catch { }
_selecting = false;
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
// Update the actual size of the selection:
if (_selecting)
{
_selection.Width = e.X - _selection.X;
_selection.Height = e.Y - _selection.Y;
// Redraw the picturebox:
pictureBox1.Refresh();
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (_selecting)
{
// Draw a rectangle displaying the current selection
Pen pen = Pens.LightSkyBlue;
e.Graphics.DrawRectangle(pen, _selection);
}
}
Output Screen
Before Cropping
After Cropping
i have picturebox1 much larger than image i'd like to load. What i want to do is aligned this image to right side, and bottom of picturebox like on screenshot:
Edit: Working
private void FromCameraPictureBox_Paint(object sender, PaintEventArgs e)
{
if (loadimage == true)
{
var image = new Bitmap(#"image.jpg");
if (image != null)
{
var g = e.Graphics;
// -- Optional -- //
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
// -- Optional -- //
g.DrawImage(image,
FromCameraPictureBox.Width - image.Width, // to right
FromCameraPictureBox.Height - image.Height, // to bottom
image.Width,
image.Height);
}
}
loadimage = false;
}
and now i want to fire paintevent from button:
void TestButtonClick(object sender, EventArgs e)
{
loadimage = true;
}
How to do this?
I'm confused why this code works nasty:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
var g = e.Graphics;
g.DrawImage(pictureBox1.Image, pictureBox1.Width - pictureBox1.Image.Width, pictureBox1.Height - pictureBox1.Image.Height);
}
EDIT:
Ok now it works:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
var image = Properties.Resources.SomeImage; //Import via Resource Manager
//Don't use pictureBox1.Image property because it will
//draw the image 2 times.
//Make sure the pictureBox1.Image property is null in Deisgn Mode
if (image != null)
{
var g = e.Graphics;
// -- Optional -- //
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
// -- Optional -- //
g.DrawImage(image,
pictureBox1.Width - image.Width, // to right
pictureBox1.Height - image.Height, // to bottom
image.Width,
image.Height);
}
}
UPDATE:
Working :) But is there any possibility to use this code without
PaintEventsArgs ? I was trying to add to my button flag and in paint
(if (flag==true) then execute Your code, but it doesn't do anything -
no drawing on picturebox1
That's because Paint event fires once. We need to make it redraw. The default redraw method for controls is Refresh();
Here you go:
bool flag = false;
Bitmap image = null;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (flag && image != null)
{
var g = e.Graphics;
// -- Optional -- //
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
// -- Optional -- //
g.DrawImage(image,
pictureBox1.Width - image.Width,
pictureBox1.Height - image.Height,
image.Width,
image.Height);
}
}
private void button2_Click(object sender, EventArgs e)
{
image = new Bitmap("someimage.png");
flag = true;
pictureBox1.Refresh(); //Causes it repaint.
}
//If you resize the form (and anchor/dock the picturebox)
//or just resize the picturebox then you will need this:
private void pictureBox1_Resize(object sender, EventArgs e)
{
pictureBox1.Refresh();
}
You can use Bitmap + Graphics objects and copy the portion of your picture to a new bitmap (result) that will be assigned to the picturebox:
Size resultSize = new Size(100, 100);
Bitmap result = new Bitmap(resultSize.Width, resultSize.Height);
float left = yourbitmap.Width - resultSize.Width;
float top = yourbitmap.Height - resultSize.Height;
using (Graphics g = Graphics.FromImage(result))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(yourbitmap, left, top, resultSize.Width, resultSize.Height);
g.Save();
}