Capturing an image behind a rectangle - c#

I have written a small application which will be used in my work environment for cropping images. The windows form (.NET 3.5) that contains the image has a transparent rectangle which I use for dragging over a section of an image and hitting a button to get me whatever was behind the rectangle.
Currently I am using the code below, this is causing me issues because the area that it is capturing is off by quite a few pixels, and I think it's something to do with my CopyFromScreen function.
//Pass in a rectangle
private void SnapshotImage(Rectangle rect)
{
Point ptPosition = new Point(rect.X, rect.Y);
Point ptRelativePosition;
//Get me the screen coordinates, so that I get the correct area
ptRelativePosition = PointToScreen(ptPosition);
//Create a new bitmap
Bitmap bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb);
//Sort out getting the image
Graphics g = Graphics.FromImage(bmp);
//Copy the image from screen
g.CopyFromScreen(this.Location.X + ptPosition.X, this.Location.Y + ptPosition.Y, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
//Change the image to be the selected image area
imageControl1.Image.ChangeImage(bmp);
}
If anyone can spot why when the image is redrawn its quite a bit out, I'd be eternally grateful at this point. Also, the ChangeImage function is fine - it works if I use a form as a select area but using a rectangle has jazzed things up a bit.

You've retrieved the relative position to the screen as ptRelativePosition, but you never actually use that - you add the rectangle's location to your form's location, which doesn't account for the form's border.
Here's that fixed, with a few optimizations:
// Pass in a rectangle
private void SnapshotImage(Rectangle rect)
{
// Get me the screen coordinates, so that I get the correct area
Point relativePosition = this.PointToScreen(rect.Location);
// Create a new bitmap
Bitmap bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb);
// Copy the image from screen
using(Graphics g = Graphics.FromImage(bmp)) {
g.CopyFromScreen(relativePosition, Point.Empty, bmp.Size);
}
// Change the image to be the selected image area
imageControl1.Image.ChangeImage(bmp);
}

Interestingly, this was because of the space between the main form and the control that the image was on and the toolbar at the top of the form separating the control and the top of the main form. To get around this I simply modified one line in capture screen to account for those pixels, as shown below:
g.CopyFromScreen(relativePosition.X + 2, relativePosition.Y+48, Point.Empty.X, Point.Empty.Y, bmp.Size);
Cheers

Related

CSharp Windows Form Picturebox Draw Small Image Without Quality Loss

I'm trying to create a level editor using Windows Forms for my monogame project and need to draw small pixel based images to a picture box with no quality loss when scaled. In monogame when I need to do this I can just set the draw type to PointClamp and then each pixel is drawn as is instead of being pixelated when zoomed; I was hoping for something like this via a picturebox. Right now it looks like this But I'd prefer a more crisp and clean image like this (The second is how it'll appear in monogame). I haven't uploaded any code for this, but just assume I grabbed an image from the filestream and used the bitmap constructor to scale it up (don't think that's relevent but I'll just put it out there).
Image croppedImage, image = tileMap.tileBox.Image;
var brush = new SolidBrush(Color.Black);
try { croppedImage = CropImage(image, tileMap.highlightedRect); } catch {
return; // If crop target is outside bounds of image then return
}
float scale = Math.Min(higlightedTileBox.Width / croppedImage.Width, higlightedTileBox.Height / image.Height);
var scaleWidth = (int)(higlightedTileBox.Width * scale);
var scaleHeight = (int)(higlightedTileBox.Height * scale);
try { higlightedTileBox.Image = new Bitmap(croppedImage, new Size(scaleWidth, scaleHeight)); } catch {
return; // Image couldn't be scaled or highlighted tileBox couldn't be set to desired image
}
CropImage:
private static Image CropImage(Bitmap img, Rectangle cropArea) {
return img.Clone(cropArea, img.PixelFormat);
}
private static Image CropImage(Image img, Rectangle cropArea) {
return CropImage(new Bitmap(img), cropArea);
}
The code above is my current method in it's entirety. tileMap is a form and tilebox is the picturebox within that form.image is the full spritesheet texture before being cropped to what the user has highlighted. After being cropped I attempt to set the current picturebox (highlightedTileBox's) image to a scaled up version of the cropped image.
So I got a solution by trying around a bit.
It looks like scaling images directly by size is using some sort of interpolation.
To try different interpolation modes supported by Winforms, I created a little demo.
As you can see, every label contains the name of the InterpolationMode and is followed by its resulting image. The original bitmap I used is the small one at the top.
From your question, it looks like you would like to achieve something like NearestNeighbour.
Following code scales bmp and the result is stored in bmp2. Try if that's what you want. Consider building a proper implementation if you're using this as solution (disposing unused bitmaps etc.).
I hope it helps.
Bitmap bmp = new Bitmap("test.bmp");
Bitmap bmp2;
Graphics g = Graphics.FromImage(bmp2=new Bitmap(bmp.Width * 2, bmp.Height * 2));
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.DrawImage(bmp, 0, 0, bmp.Width * 2, bmp.Height * 2);
g.Dispose();

how to extend draw area in Graphics.DrawImage c#

I have a Rectangle (rec) that contains the area in which a smaller image is contained within a larger image. I want to display this smaller image on a Picturebox. However, what I really am doing is using the smaller image as a picture detector for a larger image that is 333x324. So what I want to do is use the coordinates of the smaller image rectangle, and then draw to the Picturebox, starting from lefthand side of the rectangle, going outwards by 333 width and 324 height.
Currently my code works but it only displays the small image that was being used for detection purposes. I want it to display the smaller image + 300 width and + 300 height.
I fiddled with this code for hours and I must be doing something extremely basic wrong. If anyone can help me I would appreciate it so much!
My code for the class:
public static class Worker
{
public static void doWork(object myForm)
{
//infinitely search for maps
for (;;)
{
//match type signature for Threading
var myForm1 = (Form1)myForm;
//capture screen
Bitmap currentBitmap = new Bitmap(CaptureScreen.capture());
//detect map
Detector detector = new Detector();
Rectangle rec = detector.searchBitmap(currentBitmap, 0.1);
//if it actually found something
if(rec.Width != 0)
{
// Create the new bitmap and associated graphics object
Bitmap bmp = new Bitmap(rec.X, rec.Y);
Graphics g = Graphics.FromImage(bmp);
// Draw the specified section of the source bitmap to the new one
g.DrawImage(currentBitmap, 0,0, rec, GraphicsUnit.Pixel);
// send to the picture box &refresh;
myForm1.Invoke(new Action(() =>
{
myForm1.getPicturebox().Image = bmp;
myForm1.getPicturebox().Refresh();
myForm1.Update();
}));
// Clean up
g.Dispose();
bmp.Dispose();
}
//kill
currentBitmap.Dispose();
//do 10 times per second
System.Threading.Thread.Sleep(100);
}
}
}
If I understand correctly, the rec variable contains a rectangle with correct X and Y which identifies a rectangle with Width=333 and Height=324.
So inside the if statement, start by setting the desired size:
rec.Width = 333;
rec.Height = 324;
Then, note that the Bitmap constructor expects the width and height, so change
Bitmap bmp = new Bitmap(rec.X, rec.Y);
to
Bitmap bmp = new Bitmap(rec.Width, rec.Height);
and that's it - the rest of the code can stay the way it is now.

How to capture the screen behind of a see-through panel

I have a form, containing panel1 and panel1 is of green background and the form TransparencyKey is also set to green and now instead of panel, what's behind it shows up. So I added a button and added this code to capture the screen which I see through the panel:
Bitmap bmp = new Bitmap(panel1.Width, panel1.Height);
panel1.DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
bmp.Save(#"F:\Image.bmp");
MessageBox.Show("Save Complete!");
But the image saved is of green color only! Any way to save what's shown behind the panel1 on screen ?
You can use Graphics.CopyFromScreen method to get a screenshot of screen. Here is a method which helps you to get a screenshot of given bounds of the screen:
public Bitmap GetDesktopImage(Rectangle bounds)
{
var bm = new Bitmap(bounds.Width, bounds.Height);
using (Graphics g = Graphics.FromImage(bm))
{
g.CopyFromScreen(bounds.Location, new Point(0, 0), bounds.Size);
}
return bm;
}
Then it's enough to pass the screen bounds of the panel to the method. To get the screen bounds of the panel, you can pass its Bound property to RectangleToScreen method of its Parent:
var bounds = panel1.Parent.RectangleToScreen(panel1.Bounds);
var image = GetDesktopImage(bounds);
//image.Save(#"d:\bm.png", System.Drawing.Imaging.ImageFormat.Png);

How to save the painted control as a bitmap?

I have a picturebox (pictureBox1), and it gets lines drawn on it, from the paint event. I was wondering how to convert that drawing (with the lines) to a bitmap, and save it as a file. I've tried:
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox.Height);
pictureBox1.DrawToBitmap(bmp, pictureBox1.Bounds);
bmp.Save("MyImage.bmp");
But that is a blank image, without the lines. Does anyone know how I can save it with the lines? Thank you.
int bitmapX = 100
int bitmapY = 100
Bitmap imgToSave = new Bitmap(bitmapX, bitmapY);
Graphics gfx = Graphics.FromImage(imgToSave);
// draw on the image with
gfx.DrawLine() // or whatever you want to draw
// save the screenshot
string saveFilePath = Application.StartupPath + "\<name of the image>.png";
imgToSave.Save(saveFilePath, ImageFormat.Png);
This is a code snippet that works for me.
Don't use the PictureBox.Bounds property but instead use a simple Rectangle object/structure filled with the `PictureBox' width and height like this:
var bitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
pictureBox1.DrawToBitmap(bitmap, new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height));
bitmap.Save("c:\\temp\\image.bmp");
Using the Bounds property you are fetching the correct size of the picture box control, but depending on the position not the 0,0 coordinate, but the control position, which leads to an empty bitmap.

PictureBox performing unwanted scaling C#

I currently have a windows form app with a pictureBox in the middle of it which i am drawing various images too. The images are drawing fine except for the fact that they are all being scaled up by exactly 25%. I should also add that i am drawing everything inside a Paint method, using the PaintEventArgs to get the graphics device.
Ive made sure the SizeMode is set to Normal, ive checked over and over that the scale factor of the graphics object is 1 and all the image objects that i pass to the paint method are of the size they should be, but when they get drawn they are a different size.
I have until now just been calling g.drawImage(image, Rectangle) and passing the width and height of the image as the width and height of the Rectangle so that they are forced to be drawn at the correct size but i feel that this should be a short term fix and i am overlooking something simple.
Any help would be great, thanks in advance.
Code is as follows (only the important bits):
public class Level : PictureBox
{
...
private Image image;
...
public Level(TabPage parent, Panel propertiesPanel, ItemManager items, string levelName)
{
...
image = Image.FromFile(#"Levels/" + levelName);
Size = image.Size;
SizeMode = PictureBoxSizeMode.Normal;
MouseClick += new MouseEventHandler(level_MouseClick);
MouseMove += new MouseEventHandler(level_MouseMove);
Paint += new PaintEventHandler(level_Paint);
Invalidate();
}
private void level_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
//With the rectangle fix (drawing to correct size)
g.DrawImage(image, new Rectangle(0, 0, image.Size.Width, image.Size.Height));
////Without the fix (as i thought it should be be this is where it scales it)
//g.DrawImage(image, new Point(0, 0));
drawPlacedItems(g);
drawItemPreview(g);
}
This sounds like the HorizontalResolution and VerticalResolution properties of your image are being applied when you don't want them to, modify your code as per Jeremy's link to Image sizing issue in bitmap that ensures that HorizontalResolution and VerticalResolution are reset or ignored before calling DrawImage.

Categories

Resources