Copy an irregular area from one picture to another in C#

example

This example shows how to copy an irregular area from one picture to another. (The next post shows how to copy an irregular area to the clipboard.) This example has two main tasks: letting the user select an irregular area and copying that area onto another picture.

The following code shows how the program lets the user select an irregular area.

// For selecting an area.
private List<Point> Points = null;
private bool Selecting = false;

// Start selecting an area.
private void picSource_MouseDown(object sender, MouseEventArgs e)
{
    Points = new List<Point>();
    Selecting = true;
}

// Continue selecting the area.
private void picSource_MouseMove(object sender, MouseEventArgs e)
{
    if (!Selecting) return;
    Points.Add(new Point(e.X, e.Y));
    picSource.Invalidate();
}

// Stop selecting the area.
private void picSource_MouseUp(object sender, MouseEventArgs e)
{
    Selecting = false;

    // Copy the selected area.
    SelectedArea = GetSelectedArea(picSource.Image,
        Color.Transparent, Points);
}

When the user presses the mouse down on the picSource PictureBox, the code creates a new Points list and sets Selecting to true.

When the mouse moves over the PictureBox, the code adds a new point to the Points list and invalidates the picSource so it redraws.

When the user releases the mouse, the program sets Selecting to false to stop selecting an area. It also calls the GetSelectedArea method (described shortly) to copy the selected area into the SelectedArea bitmap.

The following code shows how the program redraws the picSource PictureBox.

// Draw the current selection if there is one.
private void picSource_Paint(object sender, PaintEventArgs e)
{
    if ((Points != null) && (Points.Count > 1))
    {
        using (Pen dashed_pen = new Pen(Color.Black))
        {
            dashed_pen.DashPattern = new float[] { 5, 5 };
            e.Graphics.DrawLines(Pens.White, Points.ToArray());
            e.Graphics.DrawLines(dashed_pen, Points.ToArray());
        }
    }
}

If the Points list contains at least 2 points, the Paint event handler draws lines connecting the points. First it draws the lines in white, and then it draws dashed black lines over them. The result is a black and white dashed line.

The following code shows how the GetSelectedArea method copies the selected area into a new bitmap.

// Copy the selected piece of the image into a new bitmap.
private Bitmap GetSelectedArea(Image source,
    Color bg_color, List<Point> points)
{
    // Make a new bitmap that has the background
    // color except in the selected area.
    Bitmap big_bm = new Bitmap(source);
    using (Graphics gr = Graphics.FromImage(big_bm))
    {
        // Set the background color.
        gr.Clear(bg_color);

        // Make a brush out of the original image.
        using (Brush br = new TextureBrush(source))
        {
            // Fill the selected area with the brush.
            gr.FillPolygon(br, points.ToArray());

            // Find the bounds of the selected area.
            Rectangle source_rect = GetPointListBounds(points);

            // Make a bitmap that only holds the selected area.
            Bitmap result = new Bitmap(
                source_rect.Width, source_rect.Height);

            // Copy the selected area to the result bitmap.
            using (Graphics result_gr = Graphics.FromImage(result))
            {
                Rectangle dest_rect = new Rectangle(0, 0,
                    source_rect.Width, source_rect.Height);
                result_gr.DrawImage(big_bm, dest_rect,
                    source_rect, GraphicsUnit.Pixel);
            }

            // Return the result.
            return result;
        }
    }
}

This code makes a new bitmap that is the same size as the original source image. It makes an associated Graphics object and uses it to fill the large image with the Transparent color.

Next the code uses the original image to make a brush. It fills the selected polygon on the new image with the brush. The result is an image that is transparent everywhere except inside the polygon, where it matches the original image.

The method finishes by copying the rectangle bounding the selected area into a new bitmap that’s sized to fit.

The following code shows the GetPointListBounds method.

// Return the bounds of the list of points.
private Rectangle GetPointListBounds(List<Point> points)
{
    int xmin = points[0].X;
    int xmax = xmin;
    int ymin = points[0].Y;
    int ymax = ymin;

    for (int i = 1; i < points.Count; i++)
    {
        if (xmin > points[i].X) xmin = points[i].X;
        if (xmax < points[i].X) xmax = points[i].X;
        if (ymin > points[i].Y) ymin = points[i].Y;
        if (ymax < points[i].Y) ymax = points[i].Y;
    }

    return new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin);
}

When you click on the destination image on the right, the following code executes.

// Copy the selected area centered at the point clicked.
private void picDestination_MouseClick(object sender,
    MouseEventArgs e)
{
    // Do nothing if we haven't selected an area.
    if (SelectedArea == null) return;

    // See where to put it.
    int x = e.X - SelectedArea.Width / 2;
    int y = e.Y - SelectedArea.Height / 2;

    using (Graphics gr = Graphics.FromImage(picDestination.Image))
    {
        Rectangle source_rect = new Rectangle(0, 0,
            SelectedArea.Width, SelectedArea.Height);
        Rectangle dest_rect = new Rectangle(x, y,
            SelectedArea.Width, SelectedArea.Height);
        gr .DrawImage(SelectedArea, dest_rect, source_rect,
            GraphicsUnit.Pixel);
    }

    picDestination.Refresh();
}

If you haven’t yet selected an ares in the picture on the left, the event handler exits. Otherwise the code copies the selected image onto the picture on the right so it’s centered where you clicked.


Download Example   Follow me on Twitter   RSS feed




This entry was posted in graphics, image processing and tagged , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *