Создание GraphicsPath из полупрозрачного растрового изображения

Я хочу создать GraphicsPath и список точек, чтобы сформировать контур непрозрачной области растрового изображения. При необходимости я могу гарантировать, что каждое изображение имеет только один сплошной набор непрозрачных пикселей. Так, например, я должен иметь возможность записывать точки по часовой стрелке или против часовой стрелки вдоль края пикселей и выполнять полный замкнутый цикл.

Скорость этого алгоритма не важна. Тем не менее, эффективность полученных точек не так важна, если я могу пропустить некоторые точки, чтобы уменьшить их в меньшем и менее сложном GraphicsPath.

Ниже я приведу свой текущий код, который отлично работает с большинством изображений. Однако некоторые более сложные изображения заканчиваются путями, которые, кажется, соединяются в неправильном порядке. Кажется, я знаю, почему это происходит, но не могу найти решения.

public static Point[] GetOutlinePoints(Bitmap image)
{
    List<Point> outlinePoints = new List<Point>();

    BitmapData bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

    byte[] originalBytes = new byte[image.Width * image.Height * 4];
    Marshal.Copy(bitmapData.Scan0, originalBytes, 0, originalBytes.Length);

    for (int x = 0; x < bitmapData.Width; x++)
    {
        for (int y = 0; y < bitmapData.Height; y++)
        {
            byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3];

            if (alpha != 0)
            {
                Point p = new Point(x, y);

                if (!ContainsPoint(outlinePoints, p))
                    outlinePoints.Add(p);

                break;
            }
        }
    }

    for (int y = 0; y < bitmapData.Height; y++)
    {
        for (int x = bitmapData.Width - 1; x >= 0; x--)
        {
            byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3];

            if (alpha != 0)
            {
                Point p = new Point(x, y);

                if (!ContainsPoint(outlinePoints, p))
                    outlinePoints.Add(p);

                break;
            }
        }
    }

    for (int x = bitmapData.Width - 1; x >= 0; x--)
    {
        for (int y = bitmapData.Height - 1; y >= 0; y--)
        {
            byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3];

            if (alpha != 0)
            {
                Point p = new Point(x, y);

                if (!ContainsPoint(outlinePoints, p))
                    outlinePoints.Add(p);

                break;
            }
        }
    }

    for (int y = bitmapData.Height - 1; y >= 0; y--)
    {
        for (int x = 0; x < bitmapData.Width; x++)
        {
            byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3];

            if (alpha != 0)
            {
                Point p = new Point(x, y);

                if (!ContainsPoint(outlinePoints, p))
                    outlinePoints.Add(p);

                break;
            }
        }
    }

    // Added to close the loop
    outlinePoints.Add(outlinePoints[0]);

    image.UnlockBits(bitmapData);

    return outlinePoints.ToArray();
}

public static bool ContainsPoint(IEnumerable<Point> points, Point value)
{
    foreach (Point p in points)
    {
        if (p == value)
            return true;
    }

    return false;
}

И когда я превращаю точки в путь:

GraphicsPath outlinePath = new GraphicsPath();
outlinePath.AddLines(_outlinePoints);

Вот пример, показывающий, что я хочу. Красный контур должен быть массивом точек, которые можно преобразовать в GraphicsPath для обнаружения попаданий, рисования контурного пера и заполнения его кистью.

Example of an Outline

6
задан Trevor Elliott 19 March 2012 в 17:18
поделиться