6

I have image which i add in my form.How can i fill part of image?

I have this

I have this

What I'm trying to achieve:

What i want have

Filburt
  • 17,626
  • 12
  • 64
  • 115
Dima Rodionov
  • 129
  • 1
  • 8
  • Google for 'FloodFill' ! [Here](https://stackoverflow.com/questions/28373615/create-custom-shape-for-button/28376826?s=4|0.2653#28376826) and [here](https://stackoverflow.com/questions/28830821/preserve-painting-after-resize-or-refresh/28834298?s=1|0.3861#28834298) are two examples.. Of course you need to know the pixel coordinates of the areas you want to fill. Simple if you use the mouse, tedious if you want to code it.. – TaW Jul 25 '17 at 09:26

1 Answers1

9

To floodfill an area you need a foodfill routine and very little else.

See this example:

enter image description here

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));
}
TaW
  • 53,122
  • 8
  • 69
  • 111
  • Thats actually amazing. – L. Guthardt Jul 25 '17 at 12:31
  • Wow,thank you.Can i change sizemode on autosize or it can't working?And what i must do if i want show all image? – Dima Rodionov Jul 25 '17 at 13:22
  • Can you help?may be you know?where can i read information about print. if i want "doing something(write text,number)" in my application and get information.then i want include this information in word documents.can it works inside my application?this information must include in word documents(in templates?but?may be, it would change)how can i combine word document,information from my application and print? – Dima Rodionov Jul 25 '17 at 14:03
  • As it was it worked with Normal or AutoSize, ie the modes with unstretched pixels starting at (0,0). I have added a little to make it work with StretchImage mode as well. Now you can work on the whole image.- If you are happy with an answer, please consider [accepting](http://stackoverflow.com/help/accepted-answer) it..! - I see that you have never done this: Go the the (invisible) checkmark at the top left, below the votes of the answer and click it! It turns green and gains us both a little reputation. – TaW Jul 25 '17 at 14:27
  • - As for printing, and office interop: These are other questions altogether. I don't do office, thogh.. – TaW Jul 25 '17 at 14:29
  • ok,i did it.Thank you.Can you tell me what may be i may read about this graphics and how can i use and create all graphics on c# with every step , full description and examples. Do you now sql,db?may i ask some questions? – Dima Rodionov Jul 25 '17 at 20:33