Я хочу создать 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 для обнаружения попаданий, рисования контурного пера и заполнения его кистью.