I have image which i add in my form.How can i fill part of image?
I have this
What I’m trying to achieve:
Answer
To floodfill an area you need a foodfill routine and very little else.
See this example:
It uses two pictureboxes, also a label to display the chosen color.
And two mouse click events, one to pick the color:
private void pictureBoxPalette_MouseClick(object sender, MouseEventArgs e) { Point sPt = scaledPoint(pictureBoxPalette, e.Location); lbl_color.BackColor = ((Bitmap)pictureBoxPalette.Image).GetPixel(sPt.X, sPt.Y); }
..and one to call the fill:
private void pictureBoxTgt_MouseClick(object sender, MouseEventArgs e) { Point sPt = scaledPoint(pictureBoxTgt, e.Location); Bitmap bmp = (Bitmap)pictureBoxTgt.Image; Color c0 = bmp.GetPixel(sPt.X, sPt.Y); Fill4(bmp, sPt, c0, lbl_color.BackColor); pictureBoxTgt.Image = bmp; }
The Floodfill routine is taken from this post; it is basically a direct implementation of a wikipedia algorithm..:
static void Fill4(Bitmap bmp, Point pt, Color c0, Color c1) { Color cx = bmp.GetPixel(pt.X, pt.Y); if (cx.GetBrightness() < 0.01f) return; // optional, to prevent filling a black grid Rectangle bmpRect = new Rectangle(Point.Empty, bmp.Size); Stack<Point> stack = new Stack<Point>(); int x0 = pt.X; int y0 = pt.Y; stack.Push(new Point(x0, y0) ); while (stack.Any() ) { Point p = stack.Pop(); if (!bmpRect.Contains(p)) continue; cx = bmp.GetPixel(p.X, p.Y); if (cx.ToArgb() == c0.ToArgb()) //* { bmp.SetPixel(p.X, p.Y, c1); stack.Push(new Point(p.X, p.Y + 1)); stack.Push(new Point(p.X, p.Y - 1)); stack.Push(new Point(p.X + 1, p.Y)); stack.Push(new Point(p.X - 1, p.Y)); } } }
Note: (*) Color equality will fail if one of the colors is a known or named color. So we need to convert to a common format..
Update
I have updated the code to include a function that will scale a mouse click location to an image pixel point; now it will work with SizeMode=StretchImage
as well, so you can work on the whole image..
static Point scaledPoint(PictureBox pb, Point pt) { float scaleX = 1f * pb.Image.Width / pb.ClientSize.Width; float scaleY = 1f * pb.Image.Height / pb.ClientSize.Height; return new Point((int)(pt.X * scaleX), (int)(pt.Y * scaleY)); }
Of course you can then save the Image.
Note that your original image is 4bpp and must be converted to 24bpp or better before coloring..
Also note that for SizeMode=Zoom
the calculations are a little more involved. Here is an example that should work with any SizeMode
.:
static Point scaledPoint(PictureBox pbox, Point pt) { Size si = pbox.Image.Size; Size sp = pbox.ClientSize; int left = 0; int top = 0; if (pbox.SizeMode == PictureBoxSizeMode.Normal || pbox.SizeMode == PictureBoxSizeMode.AutoSize) return pt; if (pbox.SizeMode == PictureBoxSizeMode.CenterImage) { left = (sp.Width - si.Width) / 2; top = (sp.Height - si.Height) / 2; return new Point(pt.X - left, pt.Y - top); } if (pbox.SizeMode == PictureBoxSizeMode.Zoom) { if (1f * si.Width / si.Height < 1f * sp.Width / sp.Height) left = (sp.Width - si.Width * sp.Height / si.Height) / 2; else top = (sp.Height - si.Height * sp.Width / si.Width) / 2; } pt = new Point(pt.X - left, pt.Y - top); float scaleX = 1f * pbox.Image.Width / (pbox.ClientSize.Width - 2 * left) ; float scaleY = 1f * pbox.Image.Height / (pbox.ClientSize.Height - 2 * top); return new Point((int)(pt.X * scaleX), (int)(pt.Y * scaleY)); }