Making screenshots very fast and out of memory - c#

I am trying to make screenshots of my screen very fast.
So, I use this code in my main class
[STAThread]
static void Main(string[] args)
{
int x = 1;
int screenshotsAmount = 0;
List<Bitmap> screenshots = new List<Bitmap>();
while (x == 1)
{
screenshots.Add(FullsizeScreenshot.makeScreenshot());
Clipboard.SetImage(screenshots[screenshotsAmount]);
Console.WriteLine("Screenshot " + screenshotsAmount + " has been made and added to the Bitmap list!");
screenshotsAmount++;
}
}
And I have a method in my .dll, that creates screenshots
// Class for making standard screenshots
public struct FullsizeScreenshot
{
// Making fullscreen screenshot
public static Bitmap makeScreenshot()
{
Bitmap screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
Graphics gfxScreenshot = Graphics.FromImage(screenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
gfxScreenshot.Dispose();
return screenshot;
}
}
Everything works correct, but when the number of screenshots becomes bigger than 109, my program crash with System.ArgumentException
An unhandled exception of type "System.ArgumentException" in System.Drawing.dll
Additional information: Invalid parameter.
This line throws it:
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
or this:
Bitmap screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
I tried to use using (Bitmap screenshot....) and .Dispose(), but it doesn't work correct, because these classes (Bitmap and Graphics) are classes and they just create links instead of making copies. As a result, when I dispose the Bitmap in makeScreenshot() it broke the Bitmap in my List.
So, what should I do? Maybe I should make a copy, but I don't know how.

Suppose you have a display of 1920x1080, that's 2,073,600 pixels, there are 4 bytes per pixel in a PixelFormat.Format32bppArgb So that's 8,294,400 bytes, or around 8 MB. 109 images would be 872 MB. Surprised that's crashing there, but you get the idea, it's too much memory.
If you want to make an animated gif, think how large that's going to be, full screen? Well I hope you're not planing on that, that's not practical for a gif. After you take the screenshot, resize it down immediately to the target resolution so it takes less memory.

Related

Take a screenshot of part of screen in c# [duplicate]

This question already has answers here:
C#: how to take a screenshot of a portion of screen
(4 answers)
Closed 1 year ago.
I want to take a screenshot of a specific part of a website using C# or .net Code, I can't create a Windows form application as it must be embedded into something else.
Here is what I got so far:
using System.Drawing.Imaging;
using System.Drawing;
using System;
using System.Windows.Forms;
try
{
//Creating a new Bitmap object
Bitmap captureBitmap = new Bitmap(1024, 768, PixelFormat.Format32bppArgb);
//Bitmap captureBitmap = new Bitmap(int width, int height, PixelFormat);
//Creating a Rectangle object which will
//capture our Current Screen
Rectangle captureRectangle = Screen.AllScreens[0].Bounds;
//Creating a New Graphics Object
Graphics captureGraphics = Graphics.FromImage(captureBitmap);
//Copying Image from The Screen
captureGraphics.CopyFromScreen(captureRectangle.Left,captureRectangle.Top,0,0,captureRectangle.Size);
//Saving the Image File (I am here Saving it in My E drive).
captureBitmap.Save(#"E:\Capture.jpg",ImageFormat.Jpeg);
//Displaying the Successfull Result
MessageBox.Show("Screen Captured");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
This code works fine as long as I am creating a windows form application, but as soon as I change it to console it doesn't work even after adding the "using System.Windows.Forms;" and it produces this error:
//Screen does not exist in the current context
So Is there any solution for this or any other way I could take a screenshot of a specific part of a website Programmatically without connecting to it at all.
Thanks in advance.
I actually found an answer one minute after posting from the suggested links "which btw didn't show in search I did for 2 days for some reason" but anyway
its This question
and its as simple as
Rectangle rect = new Rectangle(0, 0, 100, 100);
Bitmap bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bmp);
g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size,
CopyPixelOperation.SourceCopy);
bmp.Save(fileName, ImageFormat.Jpeg);

c# cut image in specific size in memory without save file

i have colde like this :
Bitmap bmp = new Bitmap(width, height);
I just take a capture of window. Now i want just resize this captured bitmap (bmp).
How can i cut my bmp for example
RECT rt = new RECT();
GetWindowRect(hwnd1, out rt);
Int32 width = rt.Right - rt.Left;
Int32 height = rt.Bottom - rt.Top;
int leftttt = rt.Left + (width - 202);
int width2 = rt.Right - leftttt;
// // I want cut like this :
//
// in x=lefttt y = rt.Top Size ( width2,height)
And later i can easy save file to check my results by: (but won't do that only for check)
bmp.Save(#"D:\test.jpg", ImageFormat.Jpeg);
EDIT: I Want just cut not resize .
When i do code :
var graph = Graphics.FromImage(scren_kurwa.Image);
graph.DrawImage(bmp.Image, 10, 10, 200, 200);
And i save it its just override my bmp screen and just take a capture just in smaller version.
I just want to cut for examaple i want show only 1/4 of width this screen and save it to file. ( just save 1/4 width not more).
EDIT 2 :
graph.CopyFromScreen(leftttt, rt.Top, 0, 0, new Size(width2, height), CopyPixelOperation.SourceCopy);
This code above just doing what i want but i don't want again copy from screen i want copy this from bmp captured before.
Please be patient for newbies . I searched forums and just can't find solution.
Thank you.
EDIT 3
I just did how you wrote :
Rectangle cropRect = new Rectangle(100,100,100,100);
Bitmap bmp1 = new Bitmap(bmp1.Image);
bmp1.Clone(cropRect, bmp.PixelFormat);
bmp1.Save(#"D:\xdddde.jpg", ImageFormat.Jpeg);
But it don't cut an image just display the same as i had bmp.
This should work for you:
Bitmap cuttedImage;
using(Bitmap originalImage = new Bitmap("filePathName"))
{
Rectangle cropRect = new Rectangle(...);
cuttedImage = originalImage .Clone(cropRect, originalBmp.PixelFormat);
}
cuttedImage.Save("filePathName", ImageFormat.Jpeg);
cuttedImage.Dispose();
Note that this will create a shallow copy of your Bitmap. In your case that does not seem to be a problem, but keep that in mind.
Also make sure to check the MSDN documentation for exception handling. Either check that the rectangle is bigger than 0 and not bigger than the original image beforehand or catch the exceptions.

Saving screenshots in a serial manner

I'm trying to capture screenshots as long as the user interrupts, but I've gone far to get a single screenshot and save it using a hard-coded filename. It goes like this:
Bitmap bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
bmpScreenshot.Save("Screenshot.jpeg", ImageFormat.Jpeg);
The problem is this enables me to take a single screenshot, and I need multiple screenshots to be saved at the same time. I can't find a suitable way to do this without hard-coding and jeopardizing the code at the same time. I would really appreciate help on this.
Thanks.
The taking a screenshot part is probably fast and needs to happen no matter what.
You can take many screenshots and save them later. You can use a ConcurrentQueue to do that:
Bitmap bmpScreenshot = Bitmap bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height,PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
_queue.Enqueue(bmpScreenshot);
And then have a thread that dequeues and saves:
index = 0;
while (_queue.Count > 0)
{
Bitmap bitmap = null;
if (_queue.TryDequeue(out bitmap))
{
bitmap.Save("Screenshot" + index + ".jpeg", ImageFormat.Jpeg);
index++;
}
}
An even more robust solution would be to use TPL Dataflow, but that may be an overkill.

C# Graphics.CopyFromScreen "parameter is not valid"

I had made an app in C# that will perform screen capture continuously and display it in a PictureBox using timer. After running for a few seconds, there was an ArgumentException.
Below is the code and the line that has the ArgumentException
private void timer1_Tick(object sender, EventArgs e)
{
Rectangle bounds = Screen.GetBounds(Point.Empty);
Graphics graphics;
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
using (graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(0, 0, 0, 0, new Size(bounds.Width , bounds.Height )); // ArgumentException
pictureBox1.Image = bitmap;
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
Besides that, I had notices that an alert saying low memory from Windows after running the app for a few seconds.
Any tips on resolving this problem?
You keep setting a new bitmap to the picturebox, and the previous bitmap is never disposed. After a while, the system runs short of GDI handles and/or memory (running your code, I consumed one gig of memory in under 15 seconds).
You can simply reuse your existing bitmap:
Rectangle bounds = Screen.GetBounds(Point.Empty);
Image bitmap = pictureBox1.Image ?? new Bitmap(bounds.Width, bounds.Height);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(0, 0, 0, 0, new Size(bounds.Width, bounds.Height));
if (pictureBox1.Image == null)
{
pictureBox1.Image = bitmap;
}
else
{
pictureBox1.Refresh();
}
}
You also don't have to reset pictureBox1.SizeMode on each iteration.
Alternatively, you can dispose the previous bitmap manually:
Rectangle bounds = Screen.GetBounds(Point.Empty);
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(0, 0, 0, 0, new Size(bounds.Width, bounds.Height));
using (Image prev_bitmap = pictureBox1.Image)
{
pictureBox1.Image = bitmap;
}
}
pictureBox1.Image = bitmap;
Yes, your program won't last long when you frequently update the picture box. The Bitmap class is the singular .NET class where IDisposable can't easily be ignored. It is like an iceberg, bitmaps can use massive amounts of unmanaged memory but very little managed memory. You must dispose bitmaps when you no longer use them to prevent them from consuming all available unmanaged memory for their pixel data. The garbage collector tends to hide that problem but it can't do so when it doesn't run frequently enough. And the managed portion of Bitmap is too small to trigger a collection often enough. Fix:
if (pictureBox1.Image != null) pictureBox1.Image.Dispose();
pictureBox1.Image = bitmap;
The following KB can help understanding this issue:
Bitmap and Image constructor dependencies
GDI+, and therefore the System.Drawing namespace, may defer the
decoding of raw image bits until the bits are required by the image.
Additionally, even after the image has been decoded, GDI+ may
determine that it is more efficient to discard the memory for a large
Bitmap and to re-decode later. Therefore, GDI+ must have access to the
source bits for the image for the life of the Bitmap or the Image
object.
To retain access to the source bits, GDI+ locks any source file, and
forces the application to maintain the life of any source stream, for
the life of the Bitmap or the Image object.
One has to figure out when is the object ready for disposal.

C# method CopyfFromScreen crashes after 200 uses?

For my program I need a method which takes a screenshot every half a minute. I googled and came up with this method:
public static Bitmap CaptureScreen()
{
Bitmap BMP = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics GFX = Graphics.FromImage(BMP);
GFX.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Seen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
return BMP;
}
Well all works fine for the first 200 uses of the method or so. Then the function crashes at CopyFromScreen and it says that it caused an invalid argument exception. I'm slightly confused why that is because the parameters don't change.
Could it be that the function itself just has a bug? If so are there any alternatives to take a screenshot?
Probably two separate failures to dispose. Both Graphics and Image / Bitmap implement IDisposable, so the "obvious" of the two is here:
using(Graphics GFX = Graphics.FromImage(BMP)) {
GFX.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,Seen.PrimaryScreen.Bounds.Y,
0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
}
return BMP;
However: the caller of your method should also be using the result of CaptureScreen (to release the bitmap's GDI+ handle), i.e.
using(var screen = CaptureScreen()) {
// some work here
}

Categories