I have a WinForm C# app. It displays an image 720x360 pixels size on my UserControl in the onpaint event.
I am comparing constantly the current image with the new image that comes every 1/10 second.
I had been using Validate() to force the onpaint event to redraw the new image.
What I decided to do instead was Invalidate and pass regions in.
So, I compare the current image with the new image. Look for differences. Create a new rectangle object and then use the Union method to add the new image.
This is my code:
//I use EMGU to compare 2 images.
Image<Bgr, byte> _diffBetweenCurrentAndPrevious = newImage.AbsDiff(currentFrame);
//I enumerate through the resultant image to list the differences beyond a threshold.
for (int y = 0; y < 576; y++)
{
for (int x = 0; x < 720; x++)
{
//if beyond a threshold then note it
motionRegions.Union(new Rectangle(x, y, 1, 1));
}
}
I assign the new image:
newImage =(Bitmap) currentFrame.Clone();
I then call:
Invalidate(motionRegions);
Which will call this:
protected override void OnPaint(PaintEventArgs pe)
{
Graphics g = pe.Graphics;
if (newImage != null)
{
pe.Graphics.DrawImageUnscaled(newImage, 0, 0);
}
}
The problem is I do not seem to be improving on response time / RAM when just re-drawing what I need to redraw.
Am I doing something wrong?
Thanks
Related
I'm trying to generate a custom Bitmap through code at a very small size and display it to a PictureBox, upscaled to fit said PictureBox. I am using the graphics object to do this in order to use NearestNeighbor interpolation to upscale single pixels perfectly.
I'm using the graphics object of a temporary default image that is in the PictureBoxs "Image" component on Form.Load, which is sized to be the perfect width and height to maintain the correct aspect ratio from the original Bitmap.
Here is the relevant code:
private void Form1_Load(object sender, EventArgs e)
{
bmp = new Bitmap(16, 9, PixelFormat.Format24bppRgb);
rnd = new Random();
GenerateImage();
}
private void GenerateImage()
{
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
int num = rnd.Next(2);
if (num == 0)
{
bmp.SetPixel(x, y, Color.White);
}
else
{
bmp.SetPixel(x, y, Color.Gold);
}
}
}
Bitmap image = new Bitmap(picOutput.Image);
grp = Graphics.FromImage(image);
grp.InterpolationMode = InterpolationMode.NearestNeighbor;
grp.DrawImage(
bmp,
new Rectangle(0, 0, image.Width, image.Height),
0,
0,
bmp.Width,
bmp.Height,
GraphicsUnit.Pixel
);
grp.Dispose();
picOutput.Image = image;
}
The problem is that the Bitmap seems to be drawn incorrectly. About half a pixel from the original Bitmap is cut off on the left and top edges of the Bitmap when displayed through the PictureBox, and that roughly half a pixel shows up as the original default image on the right and bottom edges. It's almost like the Bitmap was offset up and to the left while being drawn by the graphics object, it doesn't perfectly cover up the original default image like it was supposed to.
My first thought was the PictureBoxs SizeMode, which is still set to "Normal," but none of them change the problem at all. Here is a picture of the problem. The black edges on the right and bottom are part of the temporary default image (the image I used graphics from), which is completely black and covers the entire PictureBox area.
Can anyone offer some insight?
As user Jimi pointed out in a comment, grp.PixelOffsetMode = PixelOffsetMode.Half from this post solved the issue.
My problem is:
System.ComponentModel.Win32Exception: 'Error creating window handle'.
I know I can solve this problem with Dispose(), but when I use it in the program, I'm displaying another error:
System.ObjectDisposedException: 'Can not access a disposed object.
Object name: 'PictureBox'. '
I use the following code:
private void SetUpPuzzle_Click(int parts)
{
Panel P = new Panel
{
Size = new Size(200, 200),
Location = new Point(394, 62),
};
Controls.Add(P);
Control board = P;
int total = parts * parts;
var PB = new PictureBox[total];
var imgarray = new Image[total];
var img = User_Image.Image;
int W = img.Width / (int.Parse(Math.Sqrt(double.Parse(parts.ToString())).ToString()));
int H = img.Height / (int.Parse(Math.Sqrt(double.Parse(parts.ToString())).ToString()));
int size = 200 / (int.Parse(Math.Sqrt(double.Parse(parts.ToString())).ToString()));
for (int x = 0; x < parts; x++)
{
for (int y = 0; y < parts; y++)
{
var index = x * parts + y;
imgarray[index] = new Bitmap(W, H);
using (Graphics graphics = Graphics.FromImage(imgarray[index]))
graphics.DrawImage(img, new Rectangle(0, 0, W, H),
new Rectangle(x * W, y * H, W, H), GraphicsUnit.Pixel);
PB[index] = new PictureBox
{
Name = "P" + index,
Size = new Size(size, size),
Location = new Point(x * size, y * size),
Image = imgarray[index],
SizeMode = PictureBoxSizeMode.StretchImage
};
PB[index].MouseEnter += Images_M_E;
PB[index].MouseLeave += Images_M_L;
PB[index].MouseClick += Form_MouseClick;
*PB[index].Dispose();
*board.Controls.Add(PB[index]);
}
}
}
When I want to create 10,000 objects
This error is displayed.
My problem is:
System.ComponentModel.Win32Exception: 'Error creating window handle'.
Indeed. You are creating way too many controls for a Winforms application.
And disposing of them doesn't really help because you can't use a disposed object any longer..
To have this kind of large puzzle (10k pieces) you need to change from using PictureBoxes (or any other Controls) to display the puzzle pieces to a different approach. This has been suggested in the original question but then you only wanted to have 100 pieces, remember?
The most common approach is this: Keep a list of images (when they are <= 256x256 pixels do put them into an ImageList!) and draw them in the board's Paint event. This will get rid of all the overhead involved with PictureBoxes.
(Aside: One may think this will not be performant with all the DrawImage calls. But all those PictureBoxes also need to draw all the pixels on all their surfaces, so that is no issue. But they also have to carry the overhead of being (under the hood) fully functional windows (see the error message!), which is why the system can only have a limited number of them; always try to keep the number of controls < 1k!)
You will have to move the placement logic to the board's Paint event and will also have to change the event model..:
Instead of having each PictureBox respond to its own events you will have to find a way to do all the work in the board's events. This will have to be diffenrent, depending on the event.
Since we don't know which event you have and what they do and which data they need for their work, it is hard to give all the necessary details, so I'll just point out a few things..:
There will not be a Enter or Leave event you can use. Instead you need to detect entering an area of a piece by testing for it in the MouseMove event. If you keep a List<Rectangle> you can use Rectangle.Contains(e.Location) for this test.
You can detect a MouseClick but then will have to find out which area was clicked. If your Enter and Leave logic from the MouseMove is working you can use its result to know where the Click went.
Similar ideas can be used for all other events; some are simple, some need a little calculation but they will all be fast and pretty easy to implement..
To optimize performance try to make the image n the right size and use Format32bppPArgb as the pixel format, because it is faster to display.
Another option is to pull the pixel data right from the original image in the Paint event with the same calculations you use now to create them. (There is a DrawImage overlay that uses two Rectangles, one to determine the target and one for the source area..) This saves GDI handles, at least if you can't use an ImageList.
Always plan for growth! For a better implementation do create a Piece class. It should hold a Rectangle and an integer index into the ImageList's Images collection. It could also have a method Switch(Piece otherPiece) which would either switch the Rectangles or the indices.
Good luck :-)
I met this exception because endless loop creating new UI control and set its properties. After looped many times, this excption was thrown when change control visible property. I found both User Object and GDI Object (From Task Manager) are quite large.
I guess your issue is similar reason that system resources are exhaust by those UI controls.
I comment PB[index].Dispose(); and it's work.
private void SetUpPuzzle(int parts)
{
// Comment ***********
//Panel P = new Panel
//{
// Size = new Size(200, 200),
// Location = new Point(394, 62),
//};
//Controls.Add(P);
//Control board = P; ***********
int total = parts * parts;
var PB = new PictureBox[total];
var imgarray = new Image[total];
var img = User_Image.Image;
int W =Convert.ToInt32(img.Width / Math.Sqrt(parts));
int H = Convert.ToInt32(img.Height / Math.Sqrt(parts));
int size = Convert.ToInt32(200 / Math.Sqrt(parts));
for (int x = 0; x < parts; x++)
{
for (int y = 0; y < parts; y++)
{
var index = x * parts + y;
imgarray[index] = new Bitmap(W, H);
using (Graphics graphics = Graphics.FromImage(imgarray[index]))
graphics.DrawImage(img, new Rectangle(0, 0, W, H),
new Rectangle(x * W, y * H, W, H), GraphicsUnit.Pixel);
PB[index] = new PictureBox
{
Name = "P" + index,
Size = new Size(size, size),
Location = new Point(x * size, y * size),
Image = imgarray[index],
SizeMode = PictureBoxSizeMode.StretchImage
};
PB[index].MouseEnter += Form1_MouseEnter;
PB[index].MouseLeave += Form1_MouseLeave;
PB[index].MouseClick += Form1_MouseClick;
//Comment
//PB[index].Dispose(); < -----------------
// Add PB in Panel in form
panel1.Controls.Add(PB[index]);
}
}
// after add all refresh panel
panel1.Refresh();
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
throw new NotImplementedException();
}
private void Form1_MouseLeave(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private void Form1_MouseEnter(object sender, EventArgs e)
{
throw new NotImplementedException();
}
Then Call the SetUpPuzzle method in your button like :
private void button1_Click(object sender, EventArgs e)
{
SetUpPuzzle(10);
}
I'm working on a screen sharing project ,and i recieve a small blocks of image from a Socket constantly and need to update them on a certain initial dekstop bitmap i have.
Basically i constantly read data from socket(data which is stored as jpeg image) ,using Image.FromStream() to retrieve the image and copying the recieved block pixels to the full primary bitmap(at a specific position X and Y which i also get from the socket)- that's 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 inital image,which is pretty big(1920X1080 in my case).
This is my code:
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.using native memcpy.
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 isn't any method to draw current recieved the block itself on the picturebox instead of drawing the whole initial bitmap again-which seems very inefficient to me...
This is my drawing method(works extremly 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);
}
}
Here are some examples of a full primary bitmap,and other small blocks i'm getting and need to draw over the full one.
Full bitmap:
small block:
small block:
small block:
I'm getting large amount of small blocks per second(30~40) somtimes their bounds are really small(rectangle of 100X80 pixels for example) so redrawing the entire bitmap again is not necessary...Rapidly Refreshing a full screen image would kill the performance...
I hope my explaination was clear.
Looking forward for an answer.
Thanks.
It would be shame to leave that question without some answer. The following is about 10 times faster in my tests when updating small portions of the picture box. What it does basically is smart invalidating (invalidates just the updated portion of the bitmap, considering the scaling) and smart painting (draws only the invalidated portion of the picture box, taken from e.ClipRectangle and considering the scaling):
private Rectangle GetViewRect() { return pictureBox1.ClientRectangle; }
private void MainScreenThread()
{
ReadData();//reading data from socket.
initial = bufferToJpeg();//first intial full screen image.
pictureBox1.Paint += pictureBox1_Paint;//activating the paint event.
// The update action
Action<Rectangle> updateAction = imageRect =>
{
var viewRect = GetViewRect();
var scaleX = (float)viewRect.Width / initial.Width;
var scaleY = (float)viewRect.Height / initial.Height;
// Make sure the target rectangle includes the new block
var targetRect = Rectangle.FromLTRB(
(int)Math.Truncate(imageRect.X * scaleX),
(int)Math.Truncate(imageRect.Y * scaleY),
(int)Math.Ceiling(imageRect.Right * scaleX),
(int)Math.Ceiling(imageRect.Bottom * scaleY));
pictureBox1.Invalidate(targetRect);
pictureBox1.Update();
};
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.using native memcpy.
// Invoke the update action, passing the updated block rectangle
this.Invoke(updateAction, new Rectangle(x, y, block.Width, block.Height));
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
lock (initial)
{
var viewRect = GetViewRect();
var scaleX = (float)initial.Width / viewRect.Width;
var scaleY = (float)initial.Height / viewRect.Height;
var targetRect = e.ClipRectangle;
var imageRect = new RectangleF(targetRect.X * scaleX, targetRect.Y * scaleY, targetRect.Width * scaleX, targetRect.Height * scaleY);
e.Graphics.DrawImage(initial, targetRect, imageRect, GraphicsUnit.Pixel);
}
}
The only kind of tricky part is determining the scaled rectangles, especially the one for invalidating, due to floating point to int conversions required, so we make sure it's eventually a little bigger than needed, but not less.
If you just need to draw on top of the canvas, you can draw the initial image just once and then use CreateGraphics() and DrawImage to update the content:
ReadData();
initial = bufferToJpeg();
pictureBox1.Image = initial;
var graphics = pictureBox1.CreateGraphics();
while (true)
{
int pos = ReadData();
Bitmap block = bufferToJpeg();
graphics.DrawImage(block, BlockX(), BlockY());
}
I'll update the answer with a performance comparison as I'm not convinced this will give any major benefit; it will, at least, avoid a double DrawImage though.
The image has 200px width and the pictureBox has 400px.
Which property of pictureBox should I set to display the image repeat-x?
I don't think there is any property which makes the image display x times repeatedly horizontally. But a little custom code can help, here is my code:
public static class PictureBoxExtension
{
public static void SetImage(this PictureBox pic, Image image, int repeatX)
{
Bitmap bm = new Bitmap(image.Width * repeatX, image.Height);
Graphics gp = Graphics.FromImage(bm);
for (int x = 0; x <= bm.Width - image.Width; x += image.Width)
{
gp.DrawImage(image, new Point(x, 0));
}
pic.Image = bm;
}
}
//Now you can use the extension method SetImage to make the PictureBox display the image x-repeatedly
myPictureBox.SetImage(myImage, 3);//repeat 3 images horizontally.
You can customize the code to make it repeat vertically or look like a check board.
Hope it helps!
Hello i am trying to draw about 1000 images and between 10 to 100 rectangels and elipses.
But i need all of them to show up on screen only when they done loading(not in a loading screen but in a game or slideshow). so for example
texturegrass = MyApp.Properties.Resources.Grass
Rectangle[] rects;
recs = new Rectangle[1000]
for (int i = 0; i < rects.Length; i++)
{
g.DrawImage(texturegrass,rects[i]);
}
this is what i done so far but every rectangle is been drawn by it own what cause a flickering problam.
I have double bufferd the app.
I tried using parallel but the application keep crashing
I hope one of you guys can help me...
##*
You can use a Graphics object to create the image off-screen, and then draw the image on the screen using the screen's Graphics object.
var bmp = new Bitmap(MyWidth, CMyHeight);
var gOff = Graphics.FromImage(bmp);
gOff.FillRectangle(new SolidBrush(Color.White), 0, 0, bmp.Width, bmp.Height);
texturegrass = MyApp.Properties.Resources.Grass
Rectangle[] rects = ...;
recs = new Rectangle[1000]
for (int i = 0; i < rects.Length; i++) {
gOff.DrawImage(texturegrass,rects[i]);
}
At this point you can draw bmp all at once on the screen's Graphic.
Microsoft: How to Draw Images Off-Screen