MainPage.xaml.cs
// http://homepages.inf.ed.ac.uk/rbf/HIPR2/thin.htm

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;     // for WriteableBitmap


namespace Skeleton2D
{
    public struct Point
    {
        public int x;
        public int y;
    } 
    
    public partial class MainPage : UserControl
    {
        WriteableBitmap m_bitmap;
        int width = 400;
        int height = 300;
        int colorCorners, colorCircumference, colorBackground, colorInside, colorInside1, colorSkeleton;
        int colorErase;
        Point[] corners = new Point[100];
        int numberCorners = 0;
        Point insidePoint;
        int wasButtonClicked = 0;

        public MainPage()
        {
            InitializeComponent();

            text1.Text = "Left click for each corner in clockwise direction (maximally 100 points)"
                + "\n" + "Then press the button.";

            this.MouseLeftButtonDown += new MouseButtonEventHandler(MainPage_MouseLeftButtonDown);

            m_bitmap = new WriteableBitmap(width, height);
            image1.Source = m_bitmap;

            colorBackground = RGBA(0, 200, 0, 255);

            // setting the background
            for (int i = 0; i < width * height; i++)
            {
                m_bitmap.Pixels[i] = colorBackground;
            }

            colorCircumference = RGBA(200, 0, 200, 255);
            colorCorners = RGBA(0, 0, 200, 255);
            colorInside = RGBA(200, 200, 0, 255);
            colorInside1 = RGBA(200, 0, 0, 255);
            colorSkeleton = RGBA(100, 100, 50, 255);
            colorErase = RGBA(199, 199, 0, 255);
        }

        private void MainPage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            int x, y;

            switch (wasButtonClicked)
            {
                case 0:
                    {
                        x = (int)(e.GetPosition(image1).X);
                        y = (int)(e.GetPosition(image1).Y);

                        corners[numberCorners].x = x;
                        corners[numberCorners].y = y;
                        numberCorners++;

                        m_bitmap.Pixels[y * width + x] = colorCorners;
                        m_bitmap.Pixels[y * width + x + 1] = colorCorners;
                        m_bitmap.Pixels[y * width + x - 1] = colorCorners;
                        m_bitmap.Pixels[(y + 1) * width + x] = colorCorners;
                        m_bitmap.Pixels[(y - 1) * width + x] = colorCorners;
                        m_bitmap.Invalidate();
                    }
                    break;
                case 1:
                    {
                        x = (int)(e.GetPosition(image1).X);
                        y = (int)(e.GetPosition(image1).Y);

                        insidePoint.x = x;
                        insidePoint.y = y;

                        m_bitmap.Pixels[y * width + x] = colorInside1;
                        m_bitmap.Pixels[y * width + x + 1] = colorInside1;
                        m_bitmap.Pixels[y * width + x - 1] = colorInside1;
                        m_bitmap.Pixels[(y + 1) * width + x] = colorInside1;
                        m_bitmap.Pixels[(y - 1) * width + x] = colorInside1;
                        m_bitmap.Invalidate();
                    }
                    break;
            }
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            switch (wasButtonClicked)
            {
                case 0:
                    {
                        //corners[0].x = 1; corners[0].y = 1;
                        //corners[1].x = 20; corners[1].y = 1;
                        //corners[2].x = 20; corners[2].y = 10;
                        //corners[3].x = 13; corners[3].y = 10;
                        //corners[4].x = 13; corners[4].y = 15;
                        //corners[5].x = 8; corners[5].y = 15;
                        //corners[6].x = 8; corners[6].y = 10;
                        //corners[7].x = 1; corners[7].y = 10;

                        DrawLine(corners[numberCorners - 1], corners[0]);
                        m_bitmap.Pixels[corners[numberCorners - 1].y * width + corners[numberCorners - 1].x] = colorCircumference;

                        for (int i = 0; i < numberCorners - 1; i++)
                        {
                            DrawLine(corners[i], corners[i + 1]);
                            m_bitmap.Pixels[corners[i].y * width + corners[i].x] = colorCircumference;
                        }

                        m_bitmap.Invalidate();

                        text1.Text = "One left click for the internal point and then press the button.";
                        wasButtonClicked++;
                    }
                    break;
                case 1:
                    {
                        //insidePoint.x = 8; insidePoint.y = 8;
                        FillPolygon(insidePoint.x, insidePoint.y);
                        m_bitmap.Invalidate();

                        text1.Text = "Now press the button to see the skeleton.";
                        wasButtonClicked++;
                    }
                    break;
                case 2:
                    {
                        FindSkeleton();
                        m_bitmap.Invalidate();

                        text1.Text = "Refresh the page to start again.";
                    }
                    break;
            }
        }

        public void FindSkeleton()
        {
            int i, j, k;
            bool stop = false;
            int neighbours;
            int[] points = new int[width * height];

            while (stop == false)
            {
                for (k = 0; k < width * height; k++)
                {
                    points[k] = 0;
                }
                
                stop = true;
                for (i = 0; i < width; i++)
                {
                    for (j = 0; j < height; j++)
                    {
                        neighbours = 0;
                        
                        if (m_bitmap.Pixels[j * width + i] == colorCircumference)
                        {
                            if (m_bitmap.Pixels[j * width + i + 1] == colorInside)
                            {
                                neighbours++;
                                points[j * width + i + 1] = 1;
                                stop = false;
                            }
                            if (m_bitmap.Pixels[j * width + i - 1] == colorInside)
                            {
                                neighbours++;
                                points[j * width + i - 1] = 1;
                                stop = false;
                            }
                            if (m_bitmap.Pixels[(j + 1) * width + i] == colorInside)
                            {
                                neighbours++;
                                points[(j + 1) * width + i] = 1;
                                stop = false;
                            }
                            if (m_bitmap.Pixels[(j - 1) * width + i] == colorInside)
                            {
                                neighbours++;
                                points[(j - 1) * width + i] = 1;
                                stop = false;
                            }
                            if (m_bitmap.Pixels[(j + 1) * width + i + 1] == colorInside)
                            {
                                neighbours++;
                                stop = false;
                            }
                            if (m_bitmap.Pixels[(j + 1) * width + i - 1] == colorInside)
                            {
                                neighbours++;
                                stop = false;
                            }
                            if (m_bitmap.Pixels[(j - 1) * width + i + 1] == colorInside)
                            {
                                neighbours++;
                                stop = false;
                            }
                            if (m_bitmap.Pixels[(j - 1) * width + i - 1] == colorInside)
                            {
                                neighbours++;
                                stop = false;
                            }

                            if (neighbours <= 1)
                            {
                                m_bitmap.Pixels[j * width + i] = colorSkeleton;
                            }
                            else
                            {
                                // here comes code for connected skeleton
                                m_bitmap.Pixels[j * width + i] = colorErase;
                            }
                        }   // if
                    }   // loop in j
                }   // loop in i

                for (i = 0; i < width; i++)
                {
                    for (j = 0; j < height; j++)
                    {
                        if (points[j * width + i] == 1)
                        {
                            m_bitmap.Pixels[j * width + i] = colorCircumference;
                            points[j * width + i] = 0;
                        }
                    }
                }
            }
        }

        public void FillPolygon(int x, int y)     // simple recursion leaded to stack overflow
        {
            int i, j, k;
            int colorCurrent;

            colorCurrent = m_bitmap.Pixels[y * width + x];
            if ((colorCurrent == colorCircumference) || (colorCurrent == colorInside))
                return;

            i = 0;
            while (true)
            {
                colorCurrent = m_bitmap.Pixels[y * width + x + i];
                if ((colorCurrent != colorCircumference))
                {
                    m_bitmap.Pixels[y * width + x + i] = colorInside;
                    if (m_bitmap.Pixels[(y - 1) * width + x + i] == colorCircumference
                        && m_bitmap.Pixels[(y) * width + x + i - 1] == colorInside
                        && m_bitmap.Pixels[(y) * width + x + i + 1] != colorCircumference)
                    {
                        if (m_bitmap.Pixels[(y - 1) * width + x + i + 1] == colorCircumference)
                        {
                            k = 0;
                            while (m_bitmap.Pixels[(y) * width + x + i + 1 + k] == colorCircumference)
                            {
                                k++;
                            }
                            FillPolygon(x + i + 1 + k, y - 1);
                        }
                        else FillPolygon(x + i + 1, y - 1);

                    }
                    if (m_bitmap.Pixels[(y + 1) * width + x + i] == colorCircumference
                        && m_bitmap.Pixels[(y + 1) * width + x + i - 1] == colorCircumference)
                        FillPolygon(x + i - 1, y);
                    if (m_bitmap.Pixels[(y + 1) * width + x + i] == colorCircumference
                        && m_bitmap.Pixels[(y) * width + x + i - 1] == colorCircumference)
                        return;
                    if (m_bitmap.Pixels[(y) * width + x + i + 1] == colorCircumference
                        && m_bitmap.Pixels[(y + 1) * width + x + i] != colorCircumference)
                        FillPolygon(x + i, y + 1);
                    if (m_bitmap.Pixels[(y + 1) * width + x + i] == colorCircumference)
                        FillPolygon(x + i - 1, y + 1);
                    i += 1;
                }
                else
                {
                    break;
                }
            }

            j = 0;
            while (true)
            {
                colorCurrent = m_bitmap.Pixels[y * width + x - j];
                if ((colorCurrent != colorCircumference))
                {
                    m_bitmap.Pixels[y * width + x - j] = colorInside;
                    if (m_bitmap.Pixels[(y + 1) * width + x - j] == colorCircumference
                        && m_bitmap.Pixels[(y) * width + x - j + 1] == colorInside
                        && m_bitmap.Pixels[(y) * width + x - j - 1] != colorCircumference)
                    {
                        FillPolygon(x - j  - 1, y + 1);
                    }
                    if (m_bitmap.Pixels[(y - 1) * width + x - j] == colorCircumference
                        && m_bitmap.Pixels[(y - 1) * width + x - j + 1] == colorCircumference)
                    {
                        k = 0;
                        while (m_bitmap.Pixels[(y - 1) * width + x - j + k] == colorCircumference
                            && m_bitmap.Pixels[(y) * width + x - j + k] != colorCircumference)
                        {
                            k++;
                        }
                            FillPolygon(x - j + k, y - 1);
                    }
                    if (m_bitmap.Pixels[(y) * width + x - j - 1] == colorCircumference
                        && m_bitmap.Pixels[(y - 1) * width + x - j] != colorCircumference)
                        FillPolygon(x - j, y - 1);
                    if (m_bitmap.Pixels[(y - 1) * width + x - j] == colorCircumference)
                        FillPolygon(x - j + 1, y - 1);
                    j += 1;
                }
                else
                {
                    break;
                }
            }
        }

        //public void FillPolygon(int x, int y)     // simple recursion leads to stack overflow
        //{
        //    int colorCurrent;

        //    colorCurrent = m_bitmap.Pixels[y * width + x];

        //    if ( (colorCurrent == colorCircumference) | (colorCurrent == colorInside) )
        //        return;

        //    m_bitmap.Pixels[y * width + x] = colorInside;

        //    FillPolygon(x, y + 1);
        //    FillPolygon(x, y - 1);
        //    FillPolygon(x + 1, y);
        //    FillPolygon(x - 1, y);
        //}

        private void DrawLine(Point start, Point end)
        {
            float k, b;
            float x, y;
            int xint, xend, yint, yend;
            int index;

            if (start.x > end.x)
            {
                Swap(ref start.x, ref end.x);
                Swap(ref start.y, ref end.y);       // to have increasing x
            }

            if (end.x - start.x > Math.Abs(end.y - start.y))		// line: y = k * x + b
            {
                k = ((float)end.y - start.y) / (end.x - start.x);
                b = start.y - k * start.x;

                x = start.x;
                xend = end.x;

                if (k > 0)
                {
                    y = k * x + b;
                    xint = (int)x;
                    while (xint < xend)
                    {
                        index = ((int)y) * (int)(width) + xint;
                        m_bitmap.Pixels[index] = colorCircumference;
                        xint += 1;
                        y += k;
                    }
                }
                else if (k < 0)
                {
                    y = k * x + b;
                    xint = (int)x;

                    while (xint < xend)
                    {
                        index = ((int)y) * (int)(width) + xint;
                        m_bitmap.Pixels[index] = colorCircumference;
                        xint += 1;
                        y += k;
                    }
                }
                else	// k = 0
                {
                    y = b;
                    xint = (int)x;

                    while (xint < xend)
                    {
                        index = ((int)y) * (int)(width) + xint;
                        m_bitmap.Pixels[index] = colorCircumference;
                        xint += 1;
                        y += k;
                    }
                }
            }
            else		// line: x = k * y + b
            {
                if (start.y > end.y)
                {
                    Swap(ref start.x, ref end.x);
                    Swap(ref start.y, ref end.y);     // to have increasing y
                }

                k = ((float)end.x - start.x) / (end.y - start.y);
                b = start.x - k * start.y;

                y = start.y;
                yend = end.y;

                if (k > 0)
                {
                    x = k * y + b;
                    yint = (int)y;

                    while (yint < yend)
                    {
                        index = yint * (int)(width) + ((int)x);
                        m_bitmap.Pixels[index] = colorCircumference;
                        yint += 1;
                        x += k;
                    }
                }
                else if (k < 0)
                {
                    x = k * y + b;
                    yint = (int)y;

                    while (yint < yend)
                    {
                        index = yint * (int)(width) + ((int)x);
                        m_bitmap.Pixels[index] = colorCircumference;
                        yint += 1;
                        x += k;
                    }
                }
                else	// k = 0
                {
                    x = b;
                    yint = (int)y;

                    while (yint < yend)
                    {
                        index = yint * (int)(width) + ((int)x);
                        m_bitmap.Pixels[index] = colorCircumference;
                        yint += 1;
                        x += k;
                    }
                }
            }
        }

        static public void Swap(ref int a, ref int b)
        {
            int temp; 
            temp = a; a = b; b = temp;
        }

        public int RGBA(byte byRed, byte byGreen, byte byBlue, byte alpha)
        {
            return (alpha * 16777216) + (byRed * 65536) + (byGreen * 256) + byBlue;
        }
    }
}