Search code examples
c#bitmapbytescreenshot

C# Bitmap screenshot size


I'm trying to get how the size (in bytes, not in Width x Height) is calculated. I made a checking application which takes screenshot of current desktop in 24bbp state each second, then write it to MemoryStream and to byte[] with ImageConverter, then compare sizes. Each screenshot is different in size, but shouldn't it be Width x Height x 3 or smth like that? Here's testing code:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;

class Check
{
    System.Timers.Timer t;
    ImageConverter converter;
    MemoryStream ms;
    byte[] arr;
    public Check()
    {
        converter = new ImageConverter();
        t = new System.Timers.Timer();
        t.Interval = 1000;
        t.Elapsed += T_Tick;
        t.Start();
    }

    private void T_Tick(object sender, EventArgs e)
    {
        var bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
                               Screen.PrimaryScreen.Bounds.Height,
                               PixelFormat.Format24bppRgb);
        var gfxScreenshot = Graphics.FromImage(bmpScreenshot);
        gfxScreenshot.CopyFromScreen(0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size);

        ms = new MemoryStream();
        bmpScreenshot.Save(ms, ImageFormat.Jpeg);
        arr = (byte[])converter.ConvertTo(bmpScreenshot, typeof(byte[]));
        Console.WriteLine($"MS: {ms.Length}  --- byte[]: {arr.Length}");

    }
}

class Program
{
    static void Main()
    {
        new Check();
        Console.ReadLine();
    }
}

and here's the output

MS: 76638  --- byte[]: 94893
MS: 90487  --- byte[]: 107863
MS: 92424  --- byte[]: 109281
MS: 93692  --- byte[]: 110295
MS: 95222  --- byte[]: 111055
MS: 96586  --- byte[]: 112314
MS: 104584  --- byte[]: 117970
MS: 108438  --- byte[]: 120089
...and so on

So the size changes each frame it can begin to decrease eventually, but shouldn't that be static, or is there a way to achieve static size of images?

UDP: Changed format to bmp, here's another output:

MS: 3148854  --- byte[]: 104699
MS: 3148854  --- byte[]: 116002
MS: 3148854  --- byte[]: 121262
MS: 3148854  --- byte[]: 125048

so the MemoryStream is static now, but ImageConverter result is still changing.


Solution

  • This is an interesting puzzle.

    It consists of three questions:

    • Why do the numbers change for the stream?
    • Why do the numbers change for the byte array?
    • Why do the numbers for stream and array differ?

    Any compression result will depend on content; even slight changes, like a moving clock hand or an additional line in the the VS output pane will make some difference..

    Let's add another result, this time for Png and with a constant screen content:

    MS: 618997  --- byte[]: 618997
    MS: 618997  --- byte[]: 618997
    MS: 618997  --- byte[]: 618997
    MS: 618997  --- byte[]: 618997
    

    Now we can conclude:

    • The stream numbers differ as long as we don't make sure that either the screen content won't change or we use a non-compressed format like bmp. We must hide the output pane, disable any clocks with a seond hand etc..

    • The array numbers differ as long as the screen content changes

    • The stream and array numbers are the same when we store as png.

    From this we can conclude that ImageConverter stores images compressed as byte arrays in the Png format; this is nice as it saves memory. MSDN doesn't document it, though. (They probably don't want to guarantee this..)