Click here to Skip to main content
15,887,485 members
Articles / Desktop Programming / Windows Forms

Shape Control for .NET

Rate me:
Please Sign up or sign in to vote.
4.84/5 (170 votes)
23 Mar 2017CPOL10 min read 379.6K   21.6K   301  
Implementing shape control that supports transparency, custom design-time editors and simple animation
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace TraceOutline
{
    class CTraceOuline
    {


        public int color_threshold = 200;
        public bool use_red = true;
        public bool use_green = true;
        public bool use_blue = true;


        //for argb color pixel format
        private int CoordsToIndex(int x, int y, int stride)
        {
            return (stride * y) + (x * 4);
        }

        public int GetGrayScaleColor(Color c)
        {

            int numcolorplane = 3;

            numcolorplane = (!use_blue) ? numcolorplane - 1 : numcolorplane;
            numcolorplane = (!use_green) ? numcolorplane - 1 : numcolorplane;
            numcolorplane = (!use_red) ? numcolorplane - 1 : numcolorplane;

            if (numcolorplane == 0)
                return (((int)c.B + (int)c.G + (int)c.R) / 3);

            int accvalue = 0;
            accvalue = use_blue ? accvalue + (int)c.B : accvalue;
            accvalue = use_green ? accvalue + (int)c.G : accvalue;
            accvalue = use_red ? accvalue + (int)c.R : accvalue;


            return (int)(accvalue / numcolorplane);

        }

        private int GetMonoColor(Color c)
        {
            int i = GetGrayScaleColor(c);
            if (i < color_threshold)
                return 0;
            else
                return 1;
        }



        public Point[] StringOutline2Polygon(string outline)
        {
            string[] s = outline.Split(';');

            if (s.Length < 5)
                return null;

            Point[] p = new Point[s.Length - 1];

            string[] s1 = s[0].Split(',');
            for (int i = 0; i < s.Length - 1; i++)
            {
                s1 = s[i].Split(',');
                p[i].X = int.Parse(s1[0]);
                p[i].Y = int.Parse(s1[1]);

            }


            return p;
        }


        public string TraceOutlineN(Bitmap bm, int x0, int y0, int probe_width, Color fg, Color bg, bool bauto_threshold, int n)
        {

            string s = "";
            int x = 0, y = y0;
            int x1 = 0, y1 = 0;
            Color c1, c2;
            int start_direction = 0, current_direction = 0;

            int gc1 = 0;
            int gc2 = 0;
            bool hitborder = false;
            bool hitstart = false;
            int max_width = bm.Width, max_height = bm.Height;

            //direct bit manipulation
            Rectangle rect = new Rectangle(0, 0, bm.Width, bm.Height);
            BitmapData bmpData =
                     bm.LockBits(rect, ImageLockMode.ReadOnly,
                     bm.PixelFormat);
            IntPtr ptr = bmpData.Scan0;
            int bytes = bm.Width * bm.Height * 4;
            byte[] rgbValues = new byte[bytes];

            // Copy the RGB values into the array.
            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
            bm.UnlockBits(bmpData);



            if (bauto_threshold)
            {
                //get max pix value diff
                int maxpixdiff = 0;
                int maxpixvalue = 0;

                for (int i = 0; i < (probe_width * 2); i++)
                {
                    try
                    {
                        if (i % 2 == 1)
                            x = x0 + i / 2;
                        else
                            x = x0 - i / 2;


                        if (x < 0) continue;
                        if (x >= max_width) continue;

                        int index = CoordsToIndex(x, y0, bmpData.Stride);
                        if (index < 0 || index > (bytes - 1)) break;
                        c1 = Color.FromArgb(rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]);

                        gc1 = GetGrayScaleColor(c1);



                        index = CoordsToIndex(x + 1, y0, bmpData.Stride);
                        if (index < 0 || index > (bytes - 1)) break;
                        c2 = Color.FromArgb(rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]);

                        gc2 = GetGrayScaleColor(c2);

                        if (maxpixdiff < Math.Abs(gc1 - gc2))
                            maxpixdiff = Math.Abs(gc1 - gc2);

                        if (gc1 > maxpixvalue)
                            maxpixvalue = gc1;
                        if (gc2 > maxpixvalue)
                            maxpixvalue = gc2;



                    }
                    catch (Exception)
                    {
                        break;
                    }

                }

                if (maxpixdiff > 0)
                    color_threshold = maxpixvalue - (int)(0.3 * maxpixdiff);

                if (color_threshold < 0) color_threshold = 0;

            }

            int gfg = GetMonoColor(fg);
            int gbg = GetMonoColor(bg);

            for (int i = 0; i < (probe_width * 2); i++)
            {
                try
                {
                    if (i % 2 == 1)
                        x = x0 + i / 2;
                    else
                        x = x0 - i / 2;

                    if (x < 0) continue;
                    if (x >= max_width) continue;

                    int index = CoordsToIndex(x, y0, bmpData.Stride);
                    if (index < 0 || index > (bytes - 1)) break;
                    c1 = Color.FromArgb(rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]);


                    gc1 = GetMonoColor(c1);

                    index = CoordsToIndex(x + 1, y0, bmpData.Stride);
                    if (index < 0 || index > (bytes - 1)) break;
                    c2 = Color.FromArgb(rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]);

                    gc2 = GetMonoColor(c2);

                    if ((gc1 == gfg && gc2 == gbg) ||
                        (gc1 == gbg && gc2 == gfg))
                    {
                        if (gc1 == gfg && gc2 == gbg) start_direction = 4;
                        if (gc1 == gbg && gc2 == gfg) start_direction = 0;
                        hitborder = true;
                        x1 = x; y1 = y;
                        break;
                    }
                }
                catch (Exception)
                {

                    break;
                }

            }
            if (!hitborder)
            {
                return "";
            }

            Color[] cn = new Color[8 * n];
            int count = 0; int countlimit = 10000;
            x = x1; y = y1;


            int diffx = 0, diffy = 0;
            int index1 = 0;
            while (!hitstart)
            {
                count++;

                //fallback to prevent infinite loop
                if (count > countlimit)
                {

                    return "";
                }


                //getting all the neighbours' pixel color
                try
                {
                    //processing top neighbours left to right
                    for (int i = 0; i <= 2 * n; i++)
                    {
                        diffx = i - n;
                        index1 = CoordsToIndex(x + diffx, y - n, bmpData.Stride);
                        cn[i] = ((x + diffx) >= 0 && (x + diffx) < max_width && (y - n) >= 0 && (y - n) < max_height) ?
                            Color.FromArgb(rgbValues[index1 + 2], rgbValues[index1 + 1], rgbValues[index1])
                            : Color.Empty;

                    }

                    //processing right neighbours top to bottom
                    for (int i = 2 * n + 1; i < 4 * n; i++)
                    {
                        diffy = i - 3 * n;
                        index1 = CoordsToIndex(x + n, y + diffy, bmpData.Stride);

                        cn[i] = ((x + n) >= 0 && (x + n) < max_width && (y + diffy) >= 0 && (y + diffy) < max_height) ?
                         Color.FromArgb(rgbValues[index1 + 2], rgbValues[index1 + 1], rgbValues[index1])
                             : Color.Empty;

                    }

                    //processing bottom neighbours right to left					
                    for (int i = 4 * n; i <= 6 * n; i++)
                    {
                        diffx = i - 5 * n;
                        index1 = CoordsToIndex(x - diffx, y + n, bmpData.Stride);

                        cn[i] = ((x - diffx) >= 0 && (x - diffx) < max_width && (y + n) >= 0 && (y + n) < max_height) ?
                          Color.FromArgb(rgbValues[index1 + 2], rgbValues[index1 + 1], rgbValues[index1])
                            : Color.Empty;

                    }

                    //processing left neighbours bottom to top	
                    for (int i = 6 * n + 1; i < 8 * n; i++)
                    {
                        diffy = i - 7 * n;
                        index1 = CoordsToIndex(x - n, y - diffy, bmpData.Stride);
                        cn[i] = ((x - n) >= 0 && (x - n) < max_width && (y - diffy) >= 0 && (y - diffy) < max_height) ?
                         Color.FromArgb(rgbValues[index1 + 2], rgbValues[index1 + 1], rgbValues[index1])
                             : Color.Empty;

                    }


                }
                catch (Exception e)
                {
                    MessageBox.Show(e.ToString());

                    return "";

                }

                int index = 0;
                string tests = "";
                bool dir_found = false;

                //find the first valid foreground pixel				
                for (int i = start_direction; i < start_direction + (8 * n); i++)
                {
                    index = i % (8 * n);


                    if (!cn[index].Equals(Color.Empty))
                        if (GetMonoColor(cn[index]) == gfg)
                        {
                            current_direction = index;
                            dir_found = true;
                            break;
                        }

                }


                //if no foreground pixel found, just find the next valid pixel 

                if (!dir_found)
                    for (int i = start_direction; i < start_direction + (8 * n); i++)
                    {
                        index = i % (8 * n);


                        if (!cn[index].Equals(Color.Empty))
                        {
                            current_direction = index;
                            dir_found = true;
                            break;

                        }

                    }


                // find the next direction to look for foreground pixels
                if ((index >= 0) && (index <= 2 * n))
                {
                    diffx = index - n;
                    x += diffx;
                    y -= n;

                }
                if ((index > 2 * n) && (index < 4 * n))
                {
                    diffy = index - 3 * n;
                    x += n;
                    y += diffy;
                }

                if ((index >= 4 * n) && (index <= 6 * n))
                {
                    diffx = index - 5 * n;
                    x -= diffx;
                    y += n;
                }

                if ((index > 6 * n) && (index < 8 * n))
                {
                    diffy = index - 7 * n;
                    x -= n;
                    y -= diffy;
                }



                //store the found outline
                tests = x + "," + y + ";";

                s = s + tests;


                start_direction = (current_direction + (4 * n + 1)) % (8 * n);



                //adaptive stop condition

                bool bMinCountOK = (n > 1) ? (count > (max_height / 5)) : (count > 10);

                if (bMinCountOK && (Math.Abs(x - x1) < (n + 1) && (Math.Abs(y - y1) < (n + 1))))
                    hitstart = true;


            }

            return s;

        }



    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Singapore Singapore
Coder. Hacker. Fixer.

Comments and Discussions