7
\$\begingroup\$

I'm working on a screen sharing project, and I receive a small block of images from a Socket constantly. I need to update them on a certain initial desktop bitmap I have.

After receiving every block, it copies its pixels according to a specific position (X and Y parameters), which is how the initial image gets updated. But then comes the part where I need to display it on a Picturebox. I handle the Paint event and redrawing it all again. The entire initial image is pretty big (1920X1080 in my case).

private void MainScreenThread()
{
    ReadData();//reading data from socket.
    initial = bufferToJpeg();//first intial full screen image.
    pictureBox1.Paint += pictureBox1_Paint;//activating the paint event.
    while (true)
    {
        int pos = ReadData();
        x = BlockX();//where to draw :X
        y = BlockY();//where to draw :Y
        Bitmap block = bufferToJpeg();//constantly reciving blocks.
        Draw(block, new Point(x, y));//applying the changes-drawing the block on the big initial image.

        this.Invoke(new Action(() =>
        {
            pictureBox1.Refresh();//updaing the picturebox for seeing results.
            // this.Text = ((pos / 1000).ToString() + "KB");
        }));
    }
}

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    lock (initial)
    {
        e.Graphics.DrawImage(initial, pictureBox1.ClientRectangle); //draws at picturebox's bounds
    }
}

Because I'm aiming at high speed performance (it's kind of a real-time project), I would like to know if there's any method to draw the block itself on the picturebox instead of drawing the whole initial bitmap again. Plus, scaling it to the Picuturebox bounds seems too inefficient to me.

The picturebox itself is not resizeable - it has a fixed width and height. But I need to scale the initial Bitmap every time (after processing the blocks) so it will fit the picturbox bounds to allow the client to see the full image. Despite their screen resolution, it's lower than the shared screen resolution (the server screen which is getting captured and shared).

This is my drawing method (works extremely fast, copying block with memcpy):

private unsafe void Draw(Bitmap bmp2, Point point)
{
    lock (initial)
    {  
        BitmapData bmData = initial.LockBits(new Rectangle(0, 0, initial.Width, initial.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, initial.PixelFormat);
        BitmapData bmData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);
        IntPtr scan0 = bmData.Scan0;
        IntPtr scan02 = bmData2.Scan0;
        int stride = bmData.Stride;
        int stride2 = bmData2.Stride;
        int Width = bmp2.Width;
        int Height = bmp2.Height;
        int X = point.X;
        int Y = point.Y;

        scan0 = IntPtr.Add(scan0, stride * Y + X * 3);//setting the pointer to the requested line
        for (int y = 0; y < Height; y++)
        {
            memcpy(scan0, scan02 ,(UIntPtr)(Width * 3));//copy one line

            scan02 = IntPtr.Add(scan02, stride2);//advance pointers
            scan0 = IntPtr.Add(scan0, stride);//advance pointers//
        }


        initial.UnlockBits(bmData);
        bmp2.UnlockBits(bmData2);
    }
\$\endgroup\$
10
  • \$\begingroup\$ If you're looking for more speed you might be able to get it by using hardware rendering and using a DirectX surface instead of a picturebox. I'm not an expert on this, but maybe something like SharpDX could help? sharpdx.org/wiki/usage \$\endgroup\$
    – jrh
    Commented Jul 20, 2016 at 0:36
  • \$\begingroup\$ @jrh I've never worked before with sharpdx, so I extremely confused about it.. what's actually the meaning of "create surface"? is there any way to draw on picture box with sharpdx or something? I just don't understand where the sharpdx part comes in :( \$\endgroup\$
    – Slashy
    Commented Jul 20, 2016 at 6:43
  • \$\begingroup\$ I'll do some research and get back to you; in the meantime this question might help a little stackoverflow.com/questions/6391911/… ; I've seen hardware rendering used for a large performance benefit for high speed image processing but that was an unmanaged program, I have to look at how to best integrate it into Winforms. \$\endgroup\$
    – jrh
    Commented Jul 20, 2016 at 11:24
  • \$\begingroup\$ @jrh awesome! I'll have a look on this question. It's alright to use unmanaged code in c# i mean im fine with that- i uses unsafe pointers for fasrer bitmap access but i realised there are faster methods:). I really appreciate your effort! Thanks! \$\endgroup\$
    – Slashy
    Commented Jul 20, 2016 at 11:59
  • \$\begingroup\$ Were you able to find a solution? I found another couple of questions that might help you: stackoverflow.com/questions/2063178/net-redraw-at-60-fps , gamedev.stackexchange.com/questions/67651/… . I looked for a GPU accelerated API that integrated really easily into Winforms but unfortunately I wasn't able to find one. (I'm also bumping up against the limits of the Window-Invalidate-Redraw loop in the message pump) \$\endgroup\$
    – jrh
    Commented Oct 14, 2016 at 11:08

1 Answer 1

3
\$\begingroup\$

If your blocks don't change size, why not create adjacent picture boxes so that you update each one individually?

If that isn't providing enough performance, remove the picture box altogether and create a custom user control where you override the OnPaint method and do your drawing inside of it.

This way, the you will pass your user control an array of bitmaps to draw and where to draw them and, when OnPaint gets called, you simply use DrawImage() to place each one at the appropriate location.

No need for you to join the bitmaps manually.

Should a new a image arrive and you want to force redrawing, simply call Invalidate() on the user control which will force OnPaint to be called. This can be optimized even further as Invalidate can be called only on a specific region of your control, allowing you to only redraw the parts that are necessary.

\$\endgroup\$
6
  • \$\begingroup\$ the blocks size are dynamic-everytime it comes with a different with and height(also different position of where to draw). about the custom control...i've never dont that before so i guess i'll need a little help :) but before doing that, i would like to show you the drawing method i use(to "merge" and draw the block on the initial image),it's extremly fast using the memcpy for copying memory. see my edited question. rembmer it should be as efficient and fast as possible,i spent alot of time for time measurement so my brain is quite dead now. look my post :) \$\endgroup\$
    – Slashy
    Commented Jul 17, 2016 at 7:38
  • \$\begingroup\$ I've update my answer accordingly. It's easier than you think, no need to manually merge bitmaps. \$\endgroup\$
    – D. Jurcau
    Commented Jul 17, 2016 at 8:00
  • \$\begingroup\$ if i just had an example of how to do this ..haha.. would you like to write the steps for me for achieving what you've pointed to? \$\endgroup\$
    – Slashy
    Commented Jul 17, 2016 at 8:28
  • \$\begingroup\$ dont forget it should be scaled at the end of the process :) \$\endgroup\$
    – Slashy
    Commented Jul 17, 2016 at 11:35
  • \$\begingroup\$ would you to expand your explaination a little bit?i've also tried to create a control with not much success...trying to override the OnPaint but actually overriding is done with the same arguments..and here i'm adding the bitmap block to draw on...hope you can help :) \$\endgroup\$
    – Slashy
    Commented Jul 22, 2016 at 11:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.