Save panel or Form as image with High quality - c#

I am currently using this code to screen capture the panel I created, but whenever I am saving it, the quality is bad. Is there any way to maintain the good quality when saving it?
I tried resizing the panel but the result is still the same.
I tried doing a normal screen shot with the snipping tool and it also has the same result with the codes I use.
Any suggestions? or help?
private void SaveControlImage(Control theControl)
{
snapCount++;
int width = panel1.Size.Width;
int height = panel1.Size.Height;
Bitmap bm = new Bitmap(width, height, PixelFormat.Format64bppPArgb);
panel1.DrawToBitmap(bm, new Rectangle(0, 0, width, height));
//bm.Save(#"D:\TestDrawToBitmap.bmp", ImageFormat.Bmp);
bm.Save(deskTopPath + #"/Offer_" + snapCount.ToString() + "_" + DateTime.Now.ToString("yyyyMMdd") + #".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
}
Like here, it looks pixelated if you compare it to what you're reading now (like this website). I tried to screen cap the form but it looks like the uploaded picture so its useless
Screenshot:

Bitmap bmp= new Bitmap(controls.Width, controls.Height-50, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics Grap = Graphics.FromImage(bmp);
Grap.CopyFromScreen(PointToScreen(controls.Location).X, PointToScreen(controls.Location).Y, 0, 0, AnhDoThi.Size, CopyPixelOperation.SourceCopy);
SaveFileDialog save = new SaveFileDialog();
save.Filter = "JPEG|*.jpg";
DialogResult tl = save.ShowDialog();
if (tl == DialogResult.OK)
{
bmp.Save(save.FileName);
MessageBox.Show("Completed !");
}

This is what I use to save a screenshot:
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
private void SaveControlAsImage(Control control, string path)
{
Bitmap bitmap = new Bitmap(control.Width, control.Height);
control.DrawToBitmap(bitmap, control.Bounds);
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite))
{
/* using ImageFormat.Png or ImageFormat.Bmp saves the image with better quality */
bitmap.Save(fs, ImageFormat.Png);
}
}
Control screenshot using Windows Snipping Tool
Control screenshot using SaveControlAsImage (ImageFormat.Png)
Control screenshot using SaveControlAsImage (ImageFormat.Jpeg)
It's not the best quality (just a little blurry), but this removes the distortion around the text and keeps the right color.
Sidenote: You're not using the parameter (theControl) passed to your method, so you might as well remove it (not recommended), or change all the calls to panel1 inside the method to theControl and call the method like
this.SaveControlImage(this.panel1);
Also, when you're accessing just a property (i.e. this.panel1.Size.Width) it's better to just call the property instead of assign it to a variable. If you're calling a method and getting a value back (which you'll be using more than once), you must assign it to a variable (i.e. int arrayCount = array.Count()).

Windows Forms is not using vector graphics to draw the user interface. Therefore, all you can get generically is to draw the control to a Bitmap instead of to the screen. This works independently of whether your control is visible on the screen, but there is not more than that. If you want a higher-resolution image from a Windows Forms control, the only way is to resize the control and hope that it supports zooming.

Related

How to get Graphic's object with out using a control in c# using GDI+?

I need to create a drawing with a circle and text embeded to it .This drawing is outputted as Image file (Jpeg/Jpg/svg/Png) using c# GDI+ .
Since i dont want to display the UI directly on a form ,how do i get the graphic's object to start drawing.
Thanks in advance.
You can create a new Bitmap passing the size for the image:
using (Bitmap myBitmap = new Bitmap(100, 100)) {
using (Graphics g = Graphics.FromImage(myBitmap)) {
// do your drawing here
}
myBitmap.Save(#"C:\path\for\image.bmp");
}
Optionally you can set the ImageFormat for the image when saving
myBitmap.Save(#"C:\path\for\image.png", ImageFormat.Png);
You create a Bitmap, and get the graphics object from it to draw on:
Bitmap myBitmap = new Bitmap(#"C:\MyImg.bmp");
Graphics g = Graphics.FromImage(myBitmap);
Note that the Bitmap does not need to be created on disk, it can be created in memory too!

Take screenshot of multiple desktops of all visible applications and forms

I'm working with a system that has 4 outputs (monitors) with e.g. 1280x1024 pixels for each output. I need a screenshot of the whole desktop and all open applications on it.
I tried GetDesktopWindow() (MSDN) but it doesn't work properly. Some forms don't shown on the captured picture.
i tried GetDesktopWindow() function but it doesn't work properly.
Of course not.
The GetDesktopWindow function returns a handle to the desktop window. It doesn't have anything to do with capturing an image of that window.
Besides, the desktop window is not the same thing as "the entire screen". It refers specifically to the desktop window. See this article for more information and what can go wrong when you abuse the handle returned by this function.
i'm working with a system that have 4 outputs (monitors) with 1280x1024(e.g) for each output. i need a screenshot from whole desktop and all open applications on it.
This is relatively simple to do in the .NET Framework using the Graphics.CopyFromScreen method. You don't even need to do any P/Invoke!
The only trick in this case is making sure that you pass the appropriate dimensions. Since you have 4 monitors, passing only the dimensions of the primary screen won't work. You need to pass the dimensions of the entire virtual screen, which contains all of your monitors. Retrieve this by querying the SystemInformation.VirtualScreen property, which returns the bounds of the virtual screen. As the documentation indicates, this is the bounds of the entire desktop on a multiple monitor system.
Sample code:
// Determine the size of the "virtual screen", which includes all monitors.
int screenLeft = SystemInformation.VirtualScreen.Left;
int screenTop = SystemInformation.VirtualScreen.Top;
int screenWidth = SystemInformation.VirtualScreen.Width;
int screenHeight = SystemInformation.VirtualScreen.Height;
// Create a bitmap of the appropriate size to receive the screenshot.
using (Bitmap bmp = new Bitmap(screenWidth, screenHeight))
{
// Draw the screenshot into our bitmap.
using (Graphics g = Graphics.FromImage(bmp))
{
g.CopyFromScreen(screenLeft, screenTop, 0, 0, bmp.Size);
}
// Do something with the Bitmap here, like save it to a file:
bmp.Save(savePath, ImageFormat.Jpeg);
}
Edit:
please check your solution with a wpf application in a thread that is not your main thread. i tried it. it doesn't work!
Hmm, I didn't see a WPF tag on the question or mentioned anywhere in the body.
No matter, though. The code I posted works just fine in a WPF application, as long as you add the appropriate references and using declarations. You will need System.Windows.Forms and System.Drawing. There might be a more WPF-esque way of doing this that doesn't require a dependency on these WinForms assemblies, but I wouldn't know what it is.
It even works on another thread. There is nothing here that would require the UI thread.
Yes, I tested it. Here is my full test code:
using System.Windows;
using System.Windows.Forms; // also requires a reference to this assembly
using System.Drawing; // also requires a reference to this assembly
using System.Drawing.Imaging;
using System.Threading;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
// Create a new thread for demonstration purposes.
Thread thread = new Thread(() =>
{
// Determine the size of the "virtual screen", which includes all monitors.
int screenLeft = SystemInformation.VirtualScreen.Left;
int screenTop = SystemInformation.VirtualScreen.Top;
int screenWidth = SystemInformation.VirtualScreen.Width;
int screenHeight = SystemInformation.VirtualScreen.Height;
// Create a bitmap of the appropriate size to receive the screenshot.
using (Bitmap bmp = new Bitmap(screenWidth, screenHeight))
{
// Draw the screenshot into our bitmap.
using (Graphics g = Graphics.FromImage(bmp))
{
g.CopyFromScreen(screenLeft, screenTop, 0, 0, bmp.Size);
}
// Do something with the Bitmap here, like save it to a file:
bmp.Save("G:\\TestImage.jpg", ImageFormat.Jpeg);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
}
I have created a tiny helper because I needed this case today and tried many different functions. Independently of the number of monitors, you can save it as a file on the disk or store it in a binary field in db with the following code blocks.
ScreenShotHelper.cs
using System.ComponentModel;//This namespace is required for only Win32Exception. You can remove it if you are catching exceptions from another layer.
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace Company.Core.Helpers.Win32 {
public static class ScreenShotHelper {
private static Bitmap CopyFromScreen(Rectangle bounds) {
try {
var image = new Bitmap(bounds.Width, bounds.Height);
using var graphics = Graphics.FromImage(image);
graphics.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
return image;
}
catch(Win32Exception) {//When screen saver is active
return null;
}
}
public static Image Take(Rectangle bounds) {
return CopyFromScreen(bounds);
}
public static byte[] TakeAsByteArray(Rectangle bounds) {
using var image = CopyFromScreen(bounds);
using var ms = new MemoryStream();
image.Save(ms, ImageFormat.Png);
return ms.ToArray();
}
public static void TakeAndSave(string path, Rectangle bounds, ImageFormat imageFormat) {
using var image = CopyFromScreen(bounds);
image.Save(path, imageFormat);
}
}
}
Usage - Binary Field
var bounds = new Rectangle();
bounds = Screen.AllScreens.Aggregate(bounds, (current, screen)
=> Rectangle.Union(current, screen.Bounds));
_card.ScreenShot = Convert.ToBase64String(ScreenShotHelper.TakeAsByteArray(bounds));
Usage - Disk file
var bounds = new Rectangle();
bounds = Screen.AllScreens.Aggregate(bounds, (current, screen)
=> Rectangle.Union(current, screen.Bounds));
ScreenShotHelper.TakeAndSave(#"d:\screenshot.png", bounds, ImageFormat.Png);

Saving System.Drawing.Graphics to a png or bmp

I have a Graphics object that I've drawn on the screen and I need to save it to a png or bmp file. Graphics doesn't seem to support that directly, but it must be possible somehow.
What are the steps?
Here is the code:
Bitmap bitmap = new Bitmap(Convert.ToInt32(1024), Convert.ToInt32(1024), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bitmap);
// Add drawing commands here
g.Clear(Color.Green);
bitmap.Save(#"C:\Users\johndoe\test.png", ImageFormat.Png);
If your Graphics is on a form, you can use this:
private void DrawImagePointF(PaintEventArgs e)
{
... Above code goes here ...
e.Graphics.DrawImage(bitmap, 0, 0);
}
In addition, to save on a web page, you could use this:
MemoryStream memoryStream = new MemoryStream();
bitmap.Save(memoryStream, ImageFormat.Png);
var pngData = memoryStream.ToArray();
<img src="data:image/png;base64,#(Convert.ToBase64String(pngData))"/>
Graphics objects are a GDI+ drawing surface. They must have an attached device context to draw on ie either a form or an image.
Copy it to a Bitmap and then call the bitmap's Save method.
Note that if you're literally drawing to the screen (by grabbing the screen's device context), then the only way to save what you just drew to the screen is to reverse the process by drawing from the screen to a Bitmap. This is possible, but it would obviously be a lot easier to just draw directly to a Bitmap (using the same code you use to draw to the screen).
Try this, works fine for me...
private void SaveControlImage(Control ctr)
{
try
{
var imagePath = #"C:\Image.png";
Image bmp = new Bitmap(ctr.Width, ctr.Height);
var gg = Graphics.FromImage(bmp);
var rect = ctr.RectangleToScreen(ctr.ClientRectangle);
gg.CopyFromScreen(rect.Location, Point.Empty, ctr.Size);
bmp.Save(imagePath);
Process.Start(imagePath);
}
catch (Exception)
{
//
}
}
Graphics graph = CreateGraphics();
Bitmap bmpPicture = new Bitmap("filename.bmp");
graph.DrawImage(bmpPicture, width, height);
You are likely drawing either to an image or on a control. If on image use
Image.Save("myfile.png",ImageFormat.Png)
If drawing on control use Control.DrawToBitmap() and then save the returned image as above.
Thanks for the correction - I wasn't aware you can draw directly to the screen.

Need to delete a file, in use by my own app

I'm fiddling with a desktop gadget (a clock). It has a reflection effect underneath it that needs to be transparent, and I'm using CopyFromScreen to get the background, then just setting the form background to this.
Like so (part of a "dock/undock" button):
Rectangle bounds = this.Bounds;
using (Bitmap ss = new Bitmap(bounds.Width, bounds.Height))
using (Graphics g = Graphics.FromImage(ss))
{
this.Opacity = 0;
g.CopyFromScreen(this.Location, Point.Empty, bounds.Size);
ss.Save(#"C:\clock1s\bg", ImageFormat.Png);
this.Opacity = 100;
this.BackgroundImage = new Bitmap(#"C:\clock1s\bg");
g.Dispose();
}
Now, whenever I want to use this again (e.g move the clock) I'm not able to delete that file or resave it because it is currently in use. I've already tried setting the form BG to something else, but that didn't work.
How do I go about?
EDIT: Sorted, thanks (Lance McNearney).
Now, what if I had to save it to file?
Also, bonus question:
the goddamn batwatch http://upload.snelhest.org/images/100219batwatch.jpg
Selections and icons ending up underneath the form is a minor annoyance, and if possible I'd like them to either end up on top, or below (keeping the smooth anti-aliasing). I'm assuming that's way out of my leauge to fix though.
Do you really need to save the image to the file system? Can't you store it in memory just for your application (and you won't have the problem anymore)?
A MemoryStream should work as a drop-in replacement for the file path in your code (although I obviously can't compile to test it):
Rectangle bounds = this.Bounds;
using (Bitmap ss = new Bitmap(bounds.Width, bounds.Height))
using (Graphics g = Graphics.FromImage(ss))
{
this.Opacity = 0;
g.CopyFromScreen(this.Location, Point.Empty, bounds.Size);
using(MemoryStream ms = new MemoryStream()) {
ss.Save(ms, ImageFormat.Png);
this.Opacity = 100;
this.BackgroundImage = new Bitmap(ms);
}
g.Dispose();
}

Creating a jpeg from several overlapping png's

So on my site, a user has the ability to create an avatar. It's created by the user selecting from several images; there is a base "skin" image, and png's that overlap that image that depict hair, eyes, mouth, etc.
I cannot save the user's avatar to the project's files, so the user's avatar data is stored in the database, and the png's are overlapped on the fly and displayed to the user.
However, I'd like to have the ability for the user to download their avatar as a jpeg by going to a page.
I have this little example set up that is working correctly:
protected void Page_Load(object sender, EventArgs e)
{
//User has skin1.png, eyes3.png, and mouth8.png
Bitmap bit = new Bitmap(System.Drawing.Image.FromFile(Server.MapPath("/images/skin1.png")), 80, 106);
Response.ContentType = "image/jpeg";
bit.Save(Response.OutputStream, ImageFormat.Jpeg);
}
But, as you can see, I only have this working for a single image. I'd like to create a bitmap from the multiple png's and output a jpeg.
Can someone help?
You can just paint the images on top of each other. The transparency of PNG images are handled correctly, but as JPEG images doesn't have any transparency you would want a background color to draw on.
Remember to be careful to dispose of the Graphics and Bitmap objects properly. They are not even recommended to be used in a web application...
// create image to draw on
using (Bitmap bit = new Bitmap(80, 106)) {
// fill with background
bit.Clear(Color.White);
// images to load
string[] skins = new string[] { "skin1.png", "eyes3.png", "mouth8.png" };
// create graphics object for drawing on the bitmap
using (Graphics g = Graphics.FromImage(bit)) {
foreach (string skin in skins) {
// load image
using (Image skinImage = Image.FromFile(Server.MapPath("/images/" + skin)) {
// draw image
g.DrawImage(skinImage, 0, 0, 80, 106);
}
}
}
Response.ContentType = "image/jpeg";
bit.Save(Response.OutputStream, ImageFormat.Jpeg);
}
I think you want to look into Graphics.FromImage when you're overlapping images. I assume there aren't any special effects (simply overlapping and positioning each layer).
So you could have something like this:
Graphics gfxAvatar = Graphics.FromImage(bit) //bit is your base image
gfxAvatar.DrawImage(secondLayer, new Point(X,Y)); //Draw secondLayer at X, Y
Continue that with the other layers. (It might be faster to have a Using loop around the initial Graphics gfxAvatar section since there are multiple layers. Then once that's done, you can convert it to a JPG using your bit.Save method.

Categories

Resources