c# Drawing a small image on big PictureBox - c#

Imagine you are drawing a 10x10 .png picture on 700x700 PictureBox in WinForms app. The problem is the shifting picture on 0.5 pixel up and left on PictureBox.
The test picture you can watch here:
It is square with 1px width border.
And the resulting PictureBox looks like:
You can see, that border has not the same width everywhere and is shifted on 0.5 px left and up (I called px an px equivalent on PictureBox, because this px from 10x10 picture is like 100x100 real pixels).
In code I use InterpolationMode.NearestNeighbor for correct displaying small picture on big canvas. But it does not change the result.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
testBmp = new Bitmap("some_path_here\\test.png");
drawBitmapOnPictureBox(pictureBox1, testBmp);
}
private void drawBitmapOnPictureBox(PictureBox pictureBox, Bitmap bmp)
{
if (pictureBox.Size != bmp.Size)
{
float zoom = 60.0f;
Bitmap zoomed = new Bitmap((int)(bmp.Width * zoom), (int)(bmp.Height * zoom));
using (Graphics g = Graphics.FromImage(zoomed))
{
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.DrawImage(bmp, new Rectangle(Point.Empty, zoomed.Size));
}
pictureBox.Image = zoomed;
}
else
pictureBox.Image = bmp;
}
Bitmap testBmp;
}

Related

Clone jpg without losing quality in C#

Im trying to take an input from a webcam and only want the centre of the image (to passport size image).
I have a Picturebox with the webcam stream which I have set SizeMode to CentreImage. I then have a second picturebox which when I click the second picturebox i then need a cropped image i then save later (currently i get the whole original image when saving).
I have code to crop this image into the 2nd picturebox which works however it loses loads of quality. Is there a way to make this crop lossless?
private void pictureBox1_Click(object sender, EventArgs e)
{
//picPhoto.Image = pic.Image;
using (Bitmap bmp = new Bitmap(pic.Image))
{
var newImg = bmp.Clone(
new Rectangle { X = ((pic.Image.Width/2)-(350/2)), Y = ((pic.Image.Height / 2) - (450 / 2)), Width = 350, Height = 450 },
bmp.PixelFormat);
picPhoto.Image = newImg;
}
}

Image doesn't cover the whole PictureBox

I'm creating an image viewer using C# and having a small issue with scaling (zooming) PictureBox.
I have a PictureBox inside a Panel, and I can zoom (scale) an image using the controls at the top left and using mouse wheel just fine. However, at some specific zoom scales, the image doesn't cover the whole PictureBox.
For example, SO logo (100x116 pixels) at 100% and at 200%:
Image at the right is 199x131 pixels, while the PictureBox is 200x132.
I've set the BackColor of the PictureBox to Red to make the issue noticeable.
This doesn't always happen, just at specific zoom levels. Why is that? Am I doing something wrong?
I can set the BackColor of PictureBox to the BackColor of the Panel to give the illusion that the image covers the whole PictureBox, but I rather fix the problem. If I can't, I'll apply the tricky solution.
Relevant code:
float zoom = 1;
Image image = null;
public MainForm(string[] args)
{
InitializeComponent();
image = ImageBox.Image;
this.ImageBox.MouseWheel += ImageBox_MouseWheel;
if (args.Length > 0)
{
LoadImage(args[0]);
}
}
private void ImageBox_Paint(object sender, PaintEventArgs e)
{
// disable interpolation (sharper pixels)
e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
// https://msdn.microsoft.com/en-us/library/ms142046(v=vs.110).aspx
e.Graphics.DrawImage(image,
new Rectangle(0, 0, ImageBox.Width, ImageBox.Height),
0, 0, image.Width, image.Height, GraphicsUnit.Pixel);
}
private void LoadImage(string path)
{
image = Image.FromFile(path);
ImageBox.Width = (int)(image.Width * zoom);
ImageBox.Height = (int)(image.Height * zoom);
ImageBox.Image = image;
CenterImage();
}
private void ScaleImage()
{
ImageBox.Image = null;
ImageBox.Width = (int)(image.Width * zoom);
ImageBox.Height = (int)(image.Height * zoom);
ImageBox.Image = image;
CenterImage();
}
I've also created a repository, in case anyone wants to examine the app live.
You need to adjust the rectangle in ImageBox_Paint. Try this:
private void ImageBox_Paint(object sender, PaintEventArgs e)
{
int add = ImageBox.Width / 200;
// disable interpolation (sharper pixels)
e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
// https://msdn.microsoft.com/en-us/library/ms142046(v=vs.110).aspx
e.Graphics.DrawImage(image,
new Rectangle(0, 0, ImageBox.Width + add, ImageBox.Height + add),
0, 0, image.Width, image.Height, GraphicsUnit.Pixel);
}
Simply set the SizeMode property of the PictureBox to Zoom. Then adapting the size of the picturebox will automatically make the image stretch to its full size.
You don't even need that Paint event listener; it's inbuilt functionality. Just change the dimensions of the PictureBox to the calculated zoomed dimensions of the image and you're done.

Reveal portions of an image behind another image

I have a PictureBox where I display an image (Let's call it Image1). There's a second image (Image2) which needs to be revealed as the user hovers the Image1 with the mouse. I only need to reveal part of Image2 (a 10X10 pixel size box), not the whole image as the mouse moves.
Both images are BMP.
How can I accomplish this task? I would think using overlays?
I display the Image1 in the picturebox and then I load the Image2 in memory, now I just need to display the portions of the Image2 over Image1 as the mouse moves.
Thank you,
Here is an example:
public Form1()
{
InitializeComponent();
pictureBox1.Image = Bitmap.FromFile(your1stImage);
bmp = (Bitmap)Bitmap.FromFile(your2ndImage);
pb2.Parent = pictureBox1;
pb2.Size = new Size(10,10);
/* this is for fun only: It restricts the overlay to a circle:
GraphicsPath gp = new GraphicsPath();
gp.AddEllipse(pb2.ClientRectangle);
pb2.Region = new Region(gp);
*/
}
Bitmap bmp;
PictureBox pb2 = new PictureBox();
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
Rectangle rDest= pb2.ClientRectangle;
Point tLocation = new Point(e.Location.X - rDest.Width - 5,
e.Location.Y - rDest.Height - 5);
Rectangle rSrc= new Rectangle(tLocation, pb2.ClientSize);
using (Graphics G = pb2.CreateGraphics() )
{
G.DrawImage(bmp, rDest, rSrc, GraphicsUnit.Pixel);
}
pb2.Location = tLocation;
}
It uses offsets the overly to the top left of the Cursor adding a little for smooth movement..

How can I repeat a picture in pictureBox

The image has 200px width and the pictureBox has 400px.
Which property of pictureBox should I set to display the image repeat-x?
I don't think there is any property which makes the image display x times repeatedly horizontally. But a little custom code can help, here is my code:
public static class PictureBoxExtension
{
public static void SetImage(this PictureBox pic, Image image, int repeatX)
{
Bitmap bm = new Bitmap(image.Width * repeatX, image.Height);
Graphics gp = Graphics.FromImage(bm);
for (int x = 0; x <= bm.Width - image.Width; x += image.Width)
{
gp.DrawImage(image, new Point(x, 0));
}
pic.Image = bm;
}
}
//Now you can use the extension method SetImage to make the PictureBox display the image x-repeatedly
myPictureBox.SetImage(myImage, 3);//repeat 3 images horizontally.
You can customize the code to make it repeat vertically or look like a check board.
Hope it helps!

C# : GDI+ Image cropping

I have an image .I want to crop 10 px from left and 10px from right of the image.I used the below code to do so
string oldImagePath="D:\\RD\\dotnet\\Images\\photo1.jpg";
Bitmap myOriginalImage = (Bitmap)Bitmap.FromFile(oldImagePath);
int newWidth = myOriginalImage.Width;
int newHeight = myOriginalImage.Height;
Rectangle cropArea = new Rectangle(10,0, newWidth-10, newHeight);
Bitmap target = new Bitmap(cropArea.Width, cropArea.Height);
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(myOriginalImage,cropArea);
}
target.Save("D:\\RD\\dotnet\\Images\\test.jpg");
But this is not giving me the results which i expect. This outputs an image which has 10 px cropped from the right and a resized image.Instead of cropiing it is resizing the width i think.So the image is shrinked(by width). Can any one correct me ? Thanks in advance
Your new width should be reduced by twice the crop margin, since you'll be chopping off that amount from both sides.
Next, when drawing the image into the new one, draw it at a negative offset. This causes the area that you aren't interested in to be clipped off.
int cropX = 10;
Bitmap target = new Bitmap(myOriginalImage.Width - 2*cropX, myOriginalImage.Height);
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(myOriginalImage, -cropX, 0);
}
My guess is this line
Rectangle cropArea = new Rectangle(10,0, newWidth-10, newHeight);
should be
Rectangle cropArea = new Rectangle(10,0, newWidth-20, newHeight);
Set the width of the new rectangle to be 20 less than the original - 10 for each side.
Some indication what result it is giving you would be helpful in confirming this.
Corey Ross is correct. Alternately, you can translate along the negative x axis and render at 0.0, 0.0. Should produce identical results.
using (Graphics g = Graphics.FromImage(target))
{
g.TranslateTransform(-cropX, 0.0f);
g.DrawImage(myOriginalImage, 0.0f, 0.0f);
}
You need to use the overload that has you specify both Destination Rectangle, and Source Rectangle.
Below is an interactive form of this using a picture box on a form. It allows you to drag the image around. I suggest making the picture box 100 x 100 and have a much larger image such as a full screen window you've captured with alt-prtscr.
class Form1 : Form
{
// ...
Image i = new Bitmap(#"C:\Users\jasond\Pictures\foo.bmp");
Point lastLocation = Point.Empty;
Size delta = Size.Empty;
Point drawLocation = Point.Empty;
bool dragging = false;
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (!dragging)
{
lastLocation = e.Location;
dragging = true;
}
delta = new Size(lastLocation.X - e.Location.X, lastLocation.Y - e.Location.Y);
lastLocation = e.Location;
if (!delta.IsEmpty)
{
drawLocation.X += delta.Width;
drawLocation.Y += delta.Height;
pictureBox1.Invalidate();
}
}
else
{
dragging = false;
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Rectangle source = new Rectangle(drawLocation,pictureBox1.ClientRectangle.Size);
e.Graphics.DrawImage(i,pictureBox1.ClientRectangle,source, GraphicsUnit.Pixel);
}
//...
Okay, I totally fail at explaining this, but hang on:
The DrawImage function requires the location of the image, as well as it's position. You need a second position for cropping as how the old relates to the new, not vice versa.
That was entirely incomprehensible, but here is the code.
g.DrawImage(myOriginalImage, -cropArea.X, -cropArea.Y);
I hope that explains it more then I did.

Categories

Resources